C++11 模板函数特化与 SFINAE 允许进一步特化(错误 : call of overloaded function is ambiguous)

标签 c++ templates metaprogramming sfinae

这是我想要实现的目标(请参阅评论)以及到目前为止我所拥有的。
此代码是可编译和可运行的 - ideone link .

#include <type_traits>
#include <memory>
#include <iostream>

template<class T>
class ObjectMustBeCreatedType : public std::false_type {
  // needed for proper static_assert<T> below
};

// default function that has to be specialized, otherwise compiler error
template<class T>
std::shared_ptr<T> CreateObject(const std::string &path) {
    static_assert(ObjectMustBeCreatedType<T>::value, 
                  "please specialize this for your class");
}

// SFINAE to detect static T::Create function
template <
    typename T,
    typename = typename std::enable_if<
        std::is_same<
            std::shared_ptr<T>,
            decltype(T::Create(std::string{}))
        >::value
    >::type
>
std::shared_ptr<T> CreateObject(const std::string &s) {
    return T::Create(s); // if T::Create is found, call it
}

// for this class the SFINAE version should be triggered 
// and CreateObject<AutomaticClass> should be instantiated automatically
struct AutomaticClass {
    static std::shared_ptr<AutomaticClass> Create(const std::string &s) {
        std::cout << "AutomaticClass::Create" << std::endl;
        return std::make_shared<AutomaticClass>();
    }
};

// for this class CreateObject is manually specialized below
struct ManualClass {
    ManualClass(const std::string &s) { 
      std::cout << "ManualClass constructor: " << s << std::endl;
    }
};

// manual CreateObject<ManualClass> specialization
template<>
std::shared_ptr<ManualClass> CreateObject(const std::string &s) {
    std::cout << "CreateObject<ManualClass>" << std::endl;
    return std::make_shared<ManualClass>(s);
}

int main() {
    // this works
    CreateObject<ManualClass>("ManualClass test");

    // produces compile errors
    CreateObject<AutomaticClass>("AutomaticClass test");

    return 0;
}

现在的问题是,对于 SFINAE 有效的情况,现在有两个函数都与模板匹配,从而产生以下错误:

prog.cpp: In function ‘int main()’:
prog.cpp:59:59: error: call of overloaded ‘CreateObject(const char [20])’ is ambiguous
         CreateObject<AutomaticClass>("AutomaticClass test");
                                                           ^
prog.cpp:12:24: note: candidate: std::shared_ptr<_Tp1> CreateObject(const string&) [with T = AutomaticClass; std::__cxx11::string = std::__cxx11::basic_string<char>]
     std::shared_ptr<T> CreateObject(const std::string &path) {
                        ^~~~~~~~~~~~
prog.cpp:27:24: note: candidate: std::shared_ptr<_Tp1> CreateObject(const string&) [with T = AutomaticClass; <template-parameter-1-2> = void; std::__cxx11::string = std::__cxx11::basic_string<char>]
     std::shared_ptr<T> CreateObject(const std::string &s) {
                        ^~~~~~~~~~~~

如何解决这个问题:

  1. CreateObject<T>保持不变,因此进一步特化 用户类看起来尽可能干净。
  2. 因此,CreateObject<T>不得移动到类中以允许部分模板特化,这更容易做到,但看起来会很困难 脏。
  3. 默认static_assert保留错误消息,以便用户清楚地看到他需要专门化 CreateObject<T>为他的类(class)。
  4. 使用 C++11 标准而非更高标准。

最佳答案

这里的关键是您需要从一个功能模板切换到另一个功能模板,而不仅仅是启用/禁用两个功能模板之一。在后者中,您会因含糊不清而使编译器感到困惑。我觉得,老好member detector应该完成这项工作。

template <typename T>
struct has_static_member_create {
  template <typename U, std::shared_ptr<U> (*)(std::string const&)>
  struct Check;

  template <typename U>
  static std::true_type foo(Check<U, &U::Create>*);

  template <typename U>
  static std::false_type foo(...);

  constexpr static bool value = decltype(foo<T>(0))::value;
};

// More C++-11 style
// template <typename T, typename Enabled = void>
// struct has_static_member_create : std::false_type {};

// template <typename T>
// struct has_static_member_create<T,
//                                 typename std::
//                                   enable_if<std::is_same<decltype(&T::Create),
//                                                          std::shared_ptr<T> (*)(
//                                                            std::string const&)>::value>::
//                                     type> : std::true_type {};


// default function that has to be specialized, otherwise compiler error
template <typename T>
typename std::enable_if<!has_static_member_create<T>::value, std::shared_ptr<T>>::type
CreateObject(const std::string& path) {
  static_assert(ObjectMustBeCreatedType<T>::value,
                "please specialize this for your class");
}

// SFINAE to detect static T::Create function
template <typename T,
          typename = typename std::enable_if<has_static_member_create<T>::value>::type>
std::shared_ptr<T> CreateObject(const std::string& s) {
  return T::Create(s); // if T::Create is found, call it
}

// for this class the SFINAE version should be triggered
// and CreateObject<AutomaticClass> should be instantiated automatically
struct AutomaticClass {
  static std::shared_ptr<AutomaticClass> Create(const std::string& s) {
    std::cout << "AutomaticClass::Create" << std::endl;
    return std::make_shared<AutomaticClass>();
  }
};

// for this class CreateObject is manually specialized below
struct ManualClass {
  ManualClass(const std::string& s) {
    std::cout << "ManualClass constructor: " << s << std::endl;
  }
};

// manual CreateObject<ManualClass> specialization
template <>
std::shared_ptr<ManualClass> CreateObject<ManualClass>(const std::string& s) {
  std::cout << "CreateObject<ManualClass>" << std::endl;
  return std::make_shared<ManualClass>(s);
}

int main() {
  // this works
  CreateObject<ManualClass>("ManualClass test");

  // produces compile errors
  CreateObject<AutomaticClass>("AutomaticClass test");

  cerr << has_static_member_create<AutomaticClass>::value << endl;
  cerr << has_static_member_create<ManualClass>::value << endl;
  return 0;
}

随意使用检测器的其他实现和/或不同的功能模板启用/禁用技术。我所展示的只是可行的可能性之一。

关于C++11 模板函数特化与 SFINAE 允许进一步特化(错误 : call of overloaded function is ambiguous),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44468209/

相关文章:

c++ - boost::any 列表上的多态运算符

带有 std::is_base_of 的派生类的 C++ 模板函数

c++ - 我可以使 opengl 扩展指针全局化吗?

c++ - 将 Lua 嵌入 C++

c++ - 如何从 C++ 程序中打开自定义 I/O 流?

templates - Symfony2 : global variables in php templating engine

c++ - 转换表达式模板树

c++ - 有没有更好的方法来为 C++ 编写这个?

c++ - 调用默认构造函数时模板可变参数编译错误

c++ - 为什么我不能创建这样的模板函数?