15

让我们有一个名为 Y 的函数重载:

void Y(int& lvalue)
{ cout << "lvalue!" << endl; }

void Y(int&& rvalue)
{ cout << "rvalue!" << endl; }

现在,让我们定义一个类似于 std::forward 的模板函数

template<class T>
void f(T&& x)
{
   Y( static_cast<T&&>(x) );   // Using static_cast<T&&>(x) like in std::forward
}

现在看看 main()

int main()
{
   int i = 10;

   f(i);       // lvalue >> T = int&
   f(10);      // rvalue >> T = int&&
}

正如预期的那样,输出是

lvalue!
rvalue!

现在回到模板函数f()并替换static_cast<T&&>(x)static_cast<T>(x). 让我们看看输出:

lvalue!
rvalue!

一样的!为什么?如果它们相同,那么为什么要从tostd::forward<>返回一个演员表?xT&&

4

1 回答 1

23

左值与右值分类保持不变,但效果完全不同(并且值类别确实发生了变化 - 尽管在您的示例中不是以可观察的方式)。让我们回顾一下这四种情况:

template<class T>
void f(T&& x)
{
    Y(static_cast<T&&>(x));
}

template<class T>
void g(T&& x)
{
    Y(static_cast<T>(x));
}

如果我们f用左值调用,T将推断为 some X&,因此强制转换引用崩溃X& && ==> X&了,所以我们最终得到相同的左值并且没有任何变化。

如果我们f用右值调用,T将推断为 someX所以强制转换只是转换x为对 的右值引用x,所以它变成一个右值(特别是一个 xvalue)。

如果我们g用左值调用,所有相同的事情都会发生。没有必要折叠参考,因为我们只是在使用T == X&,但演员阵容仍然是空操作,我们仍然得到相同的左值。

但是如果我们g用一个右值调用,我们有static_cast<T>(x)which 会复制 x. 该副本一个右值(正如您的测试所验证的那样 - 除了现在它是一个纯右值而不是一个 xvalue),但它充其量是一个额外的、不必要的副本,T最坏的情况是编译失败(如果可移动但不可复制)。使用static_cast<T&&>(x),我们正在转换为引用,该引用不会调用副本。

这就是我们这样做的原因T&&

于 2016-07-07T17:28:06.007 回答