我有一个函数,可以通过通用引用接受任何类型,并希望为特定类型重载它(其中一些是自己模板化的,虽然我不认为这在这里很重要).不幸的是,我似乎无法以正确的顺序解决重载问题.
我会假设foo的第二个声明会更受欢迎,因为它更具体(模板化程度更低),尽管看起来我对重载解析的理解有些缺乏.有趣的是,改变第二个声明以使X取值,使得它打印“好,好”,并且通过非const引用使其成为X,使其打印“坏,好”.显然,删除第一个声明会使它返回“好,因为没有其他选择.
那么为什么会这样呢?最重要的是,如果以下代码不起作用,如何使用此签名重载函数?
#include <iostream> #include <string> class X {}; template<typename T> inline std::string foo(T && rhs) { return "bad"; } inline std::string foo(const X & rhs) { return "good"; } int main() { std::cout << foo(X()) << std::endl; X x; std::cout << foo(x) << std::endl; return 0; }
编辑:
也许更迂回的解决方案是间接地做到这一点.摆脱foo的第一种形式并使用SFINAE检查是否存在有效的过载,然后它不会调用foo_fallback.
解决方法
要回答您对于Kerre答案的问题,您可以尝试使用SFINAE:
#include <type_traits> #include <string> template <class T> struct HasFooImpl_ { template <typename C> static std::true_type test(decltype(fooImpl(std::declval<C>()))*); template <typename C> static std::false_type test(...); typedef decltype(test<T>(0)) type; }; template <typename T> using HasFooImpl = typename HasFooImpl_<T>::type; template <typename T> typename std::enable_if<HasFooImpl<T>::value,std::string>::type foo(T&& t) { return fooImpl(std::forward<T>(t)); } template <typename T> typename std::enable_if<!HasFooImpl<T>::value,std::string>::type foo(T&& t) { return "generic!"; }
您必须为任何不希望通用处理的类型实现函数fooImpl.
实现有点棘手,我首先尝试了enable_if< is_same< string,decltype(fooImpl(declval< C>()))> :: value,但对于后备,!is_same<> :: value给了我编译器错误,因为它也试图实例化decltype.
此实现有一个警告,您可能想要或可能不想使用:如果T可以转换为其他具有fooImpl定义的类型,则该转换将启动.
你可以在这里看到整个事情:http://ideone.com/3Tjtvj
更新:
如果您不想允许类型转换,它实际上变得更容易:
#include <type_traits> #include <string> template <typename T> void fooImpl(T); template <typename T> using HasFooImpl = typename std::is_same<std::string,decltype(fooImpl(std::declval<T>()))>; template <typename T> typename std::enable_if<HasFooImpl<T>::value,std::string>::type foo(T&& t) { return "generic!"; }