我看了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无关。
答案 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&
引用a
或int&
变量,或者是文字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 int
或int
,这取决于您是否考虑在表达式中发生的数组到指针转换,当您要求“类型”时。如果您要求“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 &&
如果您使用int
或enum 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()来窃取其内容。