昨天我问了a question about when to use std::forward
and when to use std::move
今天我试图应用我认为我学到的东西。我写了以下内容:
template <typename T>
void exp(T a, T b)
{
cout << "rvalues" << endl;
}
template <typename T>
void exp(T& a, T& b)
{
cout << "lvalues" << endl;
}
template <typename T>
void foo(T&& a, T&& b)
{
exp(forward<T>(a), forward<T>(b));
}
在main
我打电话foo(4, 5)
时,它打印出"rvalue"
,正如我所期望的那样,但当我做了类似的事情时
int a = 0, b = 0;
foo(a, b);
发生错误:'exp' : ambiguous call to overloaded function
我在这里缺少什么?为什么最后一次调用foo(a, b)
不会调用void exp(T& a, T& b)
函数?
答案 0 :(得分:3)
引用绑定和左值到右值转换都给出了完全匹配等级:
§13.3.3.1.4[over.ics.ref] / p1:
当引用类型的参数直接(8.5.3)绑定到参数表达式时,隐式转换序列是标识转换。
因此,编译器不能在更好的转换序列选择的基础上在两者之间进行选择,并尝试对exp
的两个重载进行部分排序(因为更专业的函数模板在重载决策中优先) 。但是:
§14.8.2.4[temp.deduct.partial] / p5:
在完成部分排序之前,会对用于部分排序的类型执行某些转换:
- 如果
P
是引用类型,P
将被引用的类型替换。- 如果
A
是引用类型,A
将被引用的类型替换。
这使得两个重载无法区分,因为它们都不是更专业的,因为从部分排序的角度看它们看起来相同,并且没有其他例外适用。
如果你的主要目标是为rvalues设置一个重载,为lvalues设置另一个重载,则可以按如下方式定义它们:
template <typename T>
void exp(T&& a, T&& b) {}
template <typename T>
void exp(T& a, T& b) {}
现在,尽管exp(T&& a, T&& b)
对于左值也是可行的,但另一个重载被视为更专业:
§14.8.2.4[temp.deduct.partial] / p9:
如果对于给定类型,推导在两个方向上都成功(即,上述转换后类型相同)并且
P
和A
都是参考类型 (在被上述类型替换之前):- 如果参数模板中的类型是左值引用,则参数中的类型 模板不是,参数类型被认为比另一个更专业;否则[...]
使exp(T& a, T& b)
成为左值的优先者,exp(T&& a, T&& b)
依次为rvalues提供。{/ p>
答案 1 :(得分:0)
您打印rvalues的exp版本不正确。 它应该是:
dir! RESETSYSTEM
如果你调用foo(a,b),编译器本可以选择复制a和b并调用你的rvalues版本,而实际上它们不是rvalues。 这就是您收到编译错误的原因。
如果你真的想看看参数是否是右值,你可以使用is_rvalue_reference:
template <typename T>
void exp(T&& a, T&& b)
{
cout << "rvalues" << endl;
}
如果你想要一个rvalues函数和一个lvalues函数,那么你可以使用enable_if:
template <typename T>
void exp(T&& a, T&& b)
{
std::cout << "rvalues: " << std::is_rvalue_reference<T&&>::value << std::endl;
}
您可能需要包含type_traits头文件。