根据参数的值类别调用函数

时间:2015-09-03 06:56:41

标签: c++ c++11

昨天我问了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)函数?

2 个答案:

答案 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:

  

如果对于给定类型,推导在两个方向上都成功(即,上述转换后类型相同)并且 PA都是参考类型 (在被上述类型替换之前):

     

- 如果参数模板中的类型是左值引用,则参数中的类型   模板不是,参数类型被认为比另一个更专业;否则[...]

使exp(T& a, T& b)成为左值的优先者,exp(T&& a, T&& b)依次为rvalues提供。{/ p>

DEMO

答案 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头文件。