C++ 具有通用引用的重载解析

C++ 具有通用引用的重载解析,c++,c++11,overloading,rvalue-reference,universal-reference,C++,C++11,Overloading,Rvalue Reference,Universal Reference,我有一个函数,它可以通过通用引用接受任何类型,并希望为特定类型重载它(其中一些类型是模板化的,尽管我认为这在这里并不重要)。不幸的是,我似乎无法以正确的顺序解决重载问题 我认为foo的第二个声明会更受欢迎,因为它更具体(模板更少),尽管看起来我对重载解析的理解有些欠缺。有趣的是,将第二个声明更改为按值获取X会使其打印“good,good”,而按非常量引用获取X会使其打印“bad,good”。显然,完全删除第一个声明会使它返回“good,good”,因为别无选择 那么为什么会发生这种情况呢?最重要

我有一个函数,它可以通过通用引用接受任何类型,并希望为特定类型重载它(其中一些类型是模板化的,尽管我认为这在这里并不重要)。不幸的是,我似乎无法以正确的顺序解决重载问题

我认为
foo
的第二个声明会更受欢迎,因为它更具体(模板更少),尽管看起来我对重载解析的理解有些欠缺。有趣的是,将第二个声明更改为按值获取
X
会使其打印“good,good”,而按非常量引用获取
X
会使其打印“bad,good”。显然,完全删除第一个声明会使它返回“good,good”,因为别无选择

那么为什么会发生这种情况呢?最重要的是,如果下面的代码不起作用,如何用这个签名重载函数

#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;
}
#包括
#包括
类X{};
模板
内联std::字符串foo(T&&rhs){
返回“坏”;
}
内联标准::字符串foo(常量X和rhs){
返回“好”;
}
int main(){

std::cout从
X
const X
的转换被认为比模板重载与
T=X
T=X&
的直接匹配更糟糕,以回答您的问题。对于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!";
}

请参见

如何使通用参考版本退居更具体版本的后面?@jleahy:您可以为
X&
X常量&
X&
创建三个非模板重载,以覆盖所有情况……是:-)编辑:。@jleahy:或者,如果您只想排除
X
,如果
在模板中(和
衰减
)@jleahy我从问题中假设你的类型不仅仅是X,对吗?我正在为这些情况寻找答案。请继续关注;)@Arne Mertz是的,想法是X&&将是一个通用的回退。我喜欢这个。同样想法的另一个实现,但是使用表达式SFINAE而不是helper类,并且
如果
(缺点是它需要另一个间接层次)。实际上,我更喜欢后者的转换语义,而且实现非常简洁。非常好。我为重载部分想出了一个解决方案:在这个问题上花1个多小时,有趣的是,它又回到了这个问题。
#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!";
}