带参考参数的重载函数模板

Cos*_*syn 4 c++ templates overloading c++11

template <typename T> void f(T&) {}
template <typename T> void f(T&&) {}
int main()
{
    int x;
    f(x);   //ambiguous
}
Run Code Online (Sandbox Code Playgroud)

为什么这个电话不明确?第一个模板特化是f <int>(int&),第二个是f <int&>(int&).由于参数相同,因此根据偏序规则更加特殊的函数模板更好.然后根据标准14.8.2.4/9

如果对于给定类型,推导在两个方向上都成功(即,在上面的转换之后类型是相同的)并且P和A都是引用类型(在被上面提到的类型替换之前):
- 如果类型来自参数模板是一个左值引用,而参数模板中的类型不是,参数类型被认为比另一个更专业; ...

第一个模板有T&,第二个模板有T &&,所以第一个应该更专业.这有什么不对?


编辑:此代码在g ++ 4.6.1和VC++ 2010 Express中测试,两者都给出了模糊错误.

How*_*ant 5

指南:

不要过载:

template <typename T> void f(T&) {}
template <typename T> void f(T&&) {}
Run Code Online (Sandbox Code Playgroud)

原因:

模式有一个特殊的模板推导规则:

template <typename T> void f(T&&) {}
Run Code Online (Sandbox Code Playgroud)

存在该规则是为了实现所谓的"完美转发".它可以完美地帮助bindmake_shared完善他们的论点,同时保留cv限定符和"值类别"(左值/右值).

这个特殊规则说,当f(T&&)使用左值参数(例如int)调用时,该T被推导为左值引用(例如int&)而不是int.并且对lvalue引用int的rvalue引用会折叠为对int的lvalue引用.即

f(x)
Run Code Online (Sandbox Code Playgroud)

电话

f<int&>(int& && x);
Run Code Online (Sandbox Code Playgroud)

这简化为:

f<int&>(int& x);
Run Code Online (Sandbox Code Playgroud)

编辑

这不是或多或少专门f<int>(int&).

感谢Johannes Schaub的更正(见评论).

方案:

您可以使用单一功能执行任何操作:

template <typename T> void f(T&&) {}
Run Code Online (Sandbox Code Playgroud)

如果T推导为左值引用,则在第一次重载时执行任何您想要执行的操作,否则在第二次重载时执行任何操作:

template <class T> void f_imp(T&, std::true_type) {std::cout << "lvalue\n";}
template <class T> void f_imp(T&&, std::false_type) {std::cout << "rvalue\n";}

template <typename T> void f(T&& x)
{
     f_imp(std::forward<T>(x), std::is_lvalue_reference<T>());
}
Run Code Online (Sandbox Code Playgroud)

并用于std::forward完美转发x到实现细节功能.