昨天我问了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':对重载函数的模糊调用
我在这里错过了什么?为什么最后一次调用 foo(a, b)
没有调用 void exp(T& a, T& b)
函数?
最佳答案
引用绑定(bind)和左值到右值的转换都被赋予了完全匹配等级:
§ 13.3.3.1.4 [over.ics.ref]/p1:
When a parameter of reference type binds directly (8.5.3) to an argument expression, the implicit conversion sequence is the identity conversion.
因此,编译器无法在更好的转换顺序选择的基础上在两者之间进行选择,而试图对exp
的两个重载进行部分排序(因为更专业的函数模板优先过载决议)。然而:
§ 14.8.2.4 [temp.deduct.partial]/p5:
Before the partial ordering is done, certain transformations are performed on the types used for partial ordering:
— If
P
is a reference type,P
is replaced by the type referred to.— If
A
is a reference type,A
is replaced by the type referred to.
这使得这两个重载无法区分,因为它们都不是更特殊的,因为从部分排序的角度来看它们看起来是一样的,并且没有其他异常(exception)适用。
如果您的主要目标是对右值进行一次重载,对左值进行一次重载,您可以按如下方式定义它们:
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:
If, for a given type, deduction succeeds in both directions (i.e., the types are identical after the transformations above) and both
P
andA
were reference types (before being replaced with the type referred to above):— if the type from the argument template was an lvalue reference and the type from the parameter template was not, the argument type is considered to be more specialized than the other; otherwise [...]
这使得 exp(T& a, T& b)
成为左值的首选,而 exp(T&& a, T&& b)
是唯一可行的右值转。
关于c++ - 根据参数的值类别调用函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32368890/