C++11 template function specialization with SFINAE allowing further specializations (error: call of overloaded function is ambiguous)
C++11 template function specialization with SFINAE allowing further specializations (error: call of overloaded function is ambiguous)
这是我正在努力完成的(请参阅评论)以及我目前所做的。
此代码是可编译和可运行的 - 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>
保持不变,因此进一步专业化
用户 classes 看起来尽可能干净。
- 因此,
CreateObject<T>
不得移动到 class 以允许部分模板特化,这更容易做到但看起来
脏。
- 保留默认
static_assert
错误消息,以便用户清楚地看到他需要为他的 class. 专门化 CreateObject<T>
- 使用 C++11 标准而不是更高的标准。
这里的关键是您需要从一个函数模板切换到另一个函数模板,而不仅仅是 enable/disable 两者之一。在后者中,您将编译器与歧义混淆。我觉得,老好人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;
}
随意使用检测器的其他实现 and/or 不同的函数模板 enabling/disabling 技术。我展示的只是可行的可能性之一。
只需使用标签分派,停止所有的专业化废话。
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
.
的命名空间中
这是我正在努力完成的(请参阅评论)以及我目前所做的。
此代码是可编译和可运行的 - 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>
保持不变,因此进一步专业化 用户 classes 看起来尽可能干净。- 因此,
CreateObject<T>
不得移动到 class 以允许部分模板特化,这更容易做到但看起来 脏。 - 保留默认
static_assert
错误消息,以便用户清楚地看到他需要为他的 class. 专门化 - 使用 C++11 标准而不是更高的标准。
CreateObject<T>
这里的关键是您需要从一个函数模板切换到另一个函数模板,而不仅仅是 enable/disable 两者之一。在后者中,您将编译器与歧义混淆。我觉得,老好人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;
}
随意使用检测器的其他实现 and/or 不同的函数模板 enabling/disabling 技术。我展示的只是可行的可能性之一。
只需使用标签分派,停止所有的专业化废话。
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
.