这是我想要完成的事情(请参阅评论)以及我到目前为止所做的事情。
此代码可编译且可运行 - 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>
。答案 0 :(得分:1)
这里的关键是你需要从一个功能模板切换到另一个功能模板,而不仅仅是启用/禁用两个功能模板中的一个。在后者中,您会混淆编译器的歧义。我认为,旧的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;
}
随意使用探测器的其他实现和/或不同的功能模板启用/禁用技术。我展示的只是其中一种可行的方法。
答案 1 :(得分:1)
只需使用标签调度并使用所有特定的废话停止它。
Create<T>(s)
执行return Create( tag<T>, s )
。现在我们编写自动的:
template<class T>
std::shared_ptr<T> CreateObject(tag_t<T>, const std::string &s) {
static_assert(std::is_same<std::shared_ptr<T>,decltype(T::Create(std::string{}))>::value,
"Please provide T::Create(string) or override `Create(tag_t<T>, string)` for your type"
);
return T::Create(s); // if T::Create is found, call it
}
当::Create
不存在时,您可以使用检测到的惯用语来改进静态断言,以获得更清晰的错误。
现在,不要专门设置CreateObject
,而是覆盖它:
std::shared_ptr<ManualClass> CreateObject(tag_t<ManualClass>, const std::string& s) {
std::cout << "CreateObject<ManualClass>" << std::endl;
return std::make_shared<ManualClass>(s);
}
最重要的是,此覆盖可以存在于ManualClass
或 tag_t
的命名空间中。