斯科特迈尔斯关于Rvalueness

时间:2013-12-20 15:33:27

标签: c++ c++11 move-semantics rvalue-reference

我看了Scott Meyers's extremely informative video on Universal References,其中我学到了大部分关于Rvalue引用,移动和转发的知识。有一次他谈论的是 rvalueness ,而不是变量的 type ,他说了一句“rvalueness独立于类型”的效果。

我知道你可以有这样的方法:

void func(MyType&& rRef)
{
    // Do domething with rRef...
}

并且此处rRef是左值,因为它可以被识别,可以采用其地址等,即使其类型为MyType&&

但rvalue不能任何类型,可以吗?我的意思是,它只能是MyType&&,对吗?从这个意义上说,我认为类型并不完全独立于右值。也许我错过了什么。

更新:我的观点可以更加清晰。如果在func()中,我调用两个重载函数中的一个定义为

void gunc(MyType&& rRef)
{
   // ...
}

void gunc(MyType& lRef)
{
   // ...
}

即。通过调用gunc(std::move(rRef))gunc(rRef),似乎括号内的结果表达式的类型与rvalueness无关。

3 个答案:

答案 0 :(得分:4)

表达式的类型没有任何引用痕迹。所以如果片刻我们假设引用可以有引用类型,那么我们将有以下

int a = 0;
int &ra = a;

int c = a + 42;
int d = ra + 42;

在上文中,表达式a 具有类型int,而表达式ra 具有类型{{ 1}}。我认为在几乎所有与表达式相关的规范规则中,例如说“表达式E必须是X型”的规则,我们必须添加“...或引用类型X”(考虑一下演员)。所以我的有根据的猜测是,这将是一个太有用的负担。


C ++具有以下类型

  • 表达式的静态类型

刚刚调用“表达式的类型”(如果没有另外指定,则表示动态表达式)。这是表达式的一个属性,它指定表达式的类型在编译时从表达式引用的中抽象出来。例如,如果int&引用aint&变量,或者是文字int,则所有这些表达式都具有0类型。

  • 左值表达式的动态类型

这是左值表达式引用的非基类对象所具有的类型。

int

在此,ofstream fs("/log"); ostream &os = fs; 包含静态类型 os动态类型 ostream

  • 对象或参考的类型

这是对象或引用实际具有的类型。对象始终具有单一类型,其类型永远不会更改。但是什么对象存在于什么位置只是在运行时才知道的东西,所以一般来说,“对象的类型”也是运行时的东西

ofstream

ostream *os; if(file) os = new ofstream("/log"); else os = new ostringstream; 表示的对象的类型(以及左值*os的动态类型)仅在运行时才知道

*os

这里,由operator new创建的数组类型也只在运行时知道,并且(感谢)确实并且无法转义到静态C ++类型系统。臭名昭着的别名规则(禁止从不兼容的左值中读取对象,粗略地说)说的是对象的“动态类型”,可能是因为它想强调运行时关注点是有意义的。但严格来说,说一个对象的“动态类型”很奇怪,因为一个对象没有“静态类型”。

  • 声明变量或成员的类型

这是您在声明中提供的类型。关于对象的类型或表达式的类型,这有时可能略有不同

int *p = new int[rand() % 5 + 1];

此处,表达式struct A { A() { } int a; }; const A *a = new const A; volatile const A *va = a; 具有类型a->a,但const int解析为的成员的声明类型具有类型a->a(成员实体)。由int表示的对象的类型具有类型a->a,因为我们使用const int表达式创建了const A对象,因此所有非静态数据成员都是隐式{ {1}}子对象。在new中,表达式具有类型const,并且声明的变量类型仍具有类型va->a,并且引用的对象的类型仍具有类型volatile const int a


当你说“a的类型”并且你将“a”声明为“int& a;”时你因此总是要说“你的类型”是什么意思。你的意思是表达吗?然后“a”的类型为int。它甚至可能变得更糟糕。想象一下“int a [10];”。这里的表达式“a”具有类型const intint,这取决于您是否考虑在表达式中发生的数组到指针转换,当您要求“类型”时。如果您要求“a”引用的变量类型,则答案唯一分别为int*int[10]


那么右值是什么类型的? Rvalues是表达式。

int

在这里,我们有rvalues int[10]int &&x = 0; int y = std::move(x); int z = x; 。两个rvalues都有0类型。 std::move(x)初始化程序中出现的表达式int是左值,即使它引用了右值x引用的同一对象。


关于使用rvalue或lvalue调用的重载函数的问题中的最后一点很有意思。仅仅因为rvalue引用被写为z并不意味着rvalues具有类型std::move(x)。它们被称为右值引用,因为您可以使用rvalues初始化它们,并且语言更喜欢初始化带有右值的左值引用。


此外,查看名称形式的rvalues

表达式可能很有用
int &&

如果您使用intenum A { X }; template<int Y> struct B { }; ,则它们是右值。但那些案例是我能想到的唯一案例。

答案 1 :(得分:2)

我认为你要忽略他的部分内容:

  

最后一点值得记住:左值或左值   表达式与其类型无关。

他解释说here。他的主要观点是:

  

表达式的类型不会告诉您它是左值还是左值   一个右值。

在他的总结发言中:

  

在类型声明中,“&amp;&amp;”表示右值引用或   通用引用 - 可以解析为左值的引用   参考或右值参考。通用参考总是有   形式T&amp;&amp;对于某些推断类型T。

     

参考折叠是导致普遍性的机制   引用(实际上只是情况下的右值引用   其中引用折叠发生的地方)有时解析为左值   引用,有时是rvalue引用。它发生在指定的   在编译期间可能出现对引用的引用的上下文。   那些上下文是模板类型推导,自动类型推导,   typedef的形成和使用,以及decltype表达式。

此处使用的类型T表示任何类型。它可能是int&&double&&Widget&& - 无关紧要。

答案 2 :(得分:0)

首先,让我们将讨论局限于简单的右值引用,并将通用引用放在一边。我们不是在谈论template <typename T> ... T &&var ...

就rvalue引用的常规Type &&var = somevalue;情况而言,我认为它的含义是:

当与参考文献绑定时,无论何种约束都是“一次性的”。它超出了范围。如果你在绑定它时修改它,没有人会知道。在它受约束时没有其它的引用。

这允许我们对rvalue引用采取一些自由,我们不能用其他类型的变量。首先想到的是使用swap()来窃取其内容。