我有一个可以通过通用引用接受任何类型的函数,并且希望为特定类型重载它(其中一些是自己模板化的,尽管我认为这不重要)。不幸的是,我似乎无法以正确的顺序解决重载问题。
我会假设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
。
答案 0 :(得分:3)
从X
到const X
的转换被认为比模板化重载与T = X
或T = X &
的直接匹配更差。
答案 1 :(得分:3)
要回答您的问题,请回答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 fooImpl(std::forward<T>(t));
}
template <typename T>
typename std::enable_if<!HasFooImpl<T>::value, std::string>::type
foo(T&& t)
{
return "generic!";
}