这是我想要实现的目标(请参阅评论)以及到目前为止我所拥有的。
此代码是可编译和可运行的 - 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) {
^~~~~~~~~~~~
如何解决这个问题:
-
CreateObject<T>
保持不变,因此进一步特化 用户类看起来尽可能干净。 - 因此,
CreateObject<T>
不得移动到类中以允许部分模板特化,这更容易做到,但看起来会很困难 脏。 - 默认
static_assert
保留错误消息,以便用户清楚地看到他需要专门化CreateObject<T>
为他的类(class)。 - 使用 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/