我过去几年一直在使用C ++编码。但有一个问题我一直没有搞清楚。我想问一下:在C++中,所有的临时变量都是右值吗?
如果不是,能否提供一个示例,其中代码产生的临时变量是左值?
我过去几年一直在使用C ++编码。但有一个问题我一直没有搞清楚。我想问一下:在C++中,所有的临时变量都是右值吗?
如果不是,能否提供一个示例,其中代码产生的临时变量是左值?
不。
C++语言规范从未像你所问的那样直截了当地做出断言。在语言标准中,它并没有明确规定“所有临时对象都是右值”。此外,问题本身有点误导,因为在C++语言中,“右值”的属性并不是一个对象的属性,而是表达式的属性(即其结果的属性)。这实际上是在语言规范中如何定义的:对于不同类型的表达式,它表明结果何时是左值,何时是右值。除其他事项外,这实际上意味着临时对象可以根据用于访问的特定表达式形式作为右值和左值进行访问。
例如,文字表达式2 + 3
的结果显然是一个右值,即类型为int
的临时对象。我们无法将一元运算符&
应用于它,因为一元运算符&
要求其操作数为左值。
&(2 + 3); // ERROR, lvalue required
然而,正如我们所知道的那样,一个常量引用可以附加到一个临时对象上,例如
const int &ri = 2 + 3;
ri
访问同一个临时变量,因为引用始终是左值。例如,我们可以轻松合法地对引用应用一元运算符&
并获得指向临时变量的指针。const int *pi = &ri;
T()
定义为“创建指定类型的rvalue”的表达式。我的理解(英语不是我的母语)是,“rvalue”被用作所创建实例的属性,而不是表达式本身的属性。 - David Rodríguez - dribeasPrasoon Saurav已经链接了一个非常好的clc++主题讨论。在那里,James Kanze解释了为什么这个问题实际上没有意义。归根结底是:
因此,这个问题就没有意义。
以下代码是一个很好的例子:
int main() {
const int& ri = 4;
std::cout << ri << std::endl;
}
值为4
的临时int并不是表达式。打印出来的表达式ri
也不是一个临时变量,它是一个左值,并引用一个临时变量。
int(3)
是一个右值。 - MSalters嗯,那个数组操作符返回一个引用,任何返回引用的函数都可以被认为是做同样的事情吗?所有的引用都是const类型,虽然它们可以是左值,但它们修改的是引用指向的对象,而不是引用本身。对于 *
操作符也是如此,
*(一个临时指针) = val;
我发誓曾经使用过某种编译器,它会将临时值传递给任何接受引用的函数,
这样你就可以这么写:
int Afunc()
{
return 5;
}
int anotherFunc(int & b)
{
b = 34;
}
anotherFunc(Afunc());
但现在找不到一个可以让你这样做的,引用必须是const才能允许传递临时值。
int anotherFunc(const int & b);
无论如何,引用可以是lvalue和临时的,诀窍在于引用本身没有被修改,只有它所引用的内容。
如果您将->
操作符视为运算符,则临时指针可以是lvalue,但同样的条件适用,不是临时指针会被更改,而是它所指向的内容。
2+2
会产生一个临时值吗? - David Rodríguez - dribeastypedef foo<2+2> FooFour
。 - MSalters2+2
从来不是临时的。它只是一个值。我对一些回答进行了负评,因为它们声称像2+2
这样的东西是临时的。但这是没有意义的。 - Johannes Schaub - litb这取决于你认为什么是临时变量。你可以写类似于
#include <stdio.h>
int main()
{
char carray[10];
char *c=carray+1;
*(c+2+4) = 9;
printf("%d\n",carray[7]);
return 0;
}
这可以在VisualStudios和GCC中运行。您可以在codepad中运行代码
我认为(c+2+4)是一个右值,尽管我想将其分配。当我对其进行解引用时,它将变成左值。因此,所有临时值都是右值。但是,您可以通过解引用将右值(因此是临时值)转换为左值。
(c+2+4)
根据定义是 rvalue。 - curiousguy*((char*)(ptr+offset))=9
是一个右值吗?如果我不解引用它,我认为这不会编译通过...好的,那么在我解引用一个右值之后,它是一个左值吗? - user34537*((char*)(ptr+offset))
是一个左值,而*((char*)(ptr+offset))=9
也是一个左值。在C语言中,只有第一个表达式是左值。"我认为如果我不解除引用它,这个代码不会编译..." (ptr+offset);
将作为一个语句编译,2+2;
也是如此。在C和C++中,结果被忽略的没有效果的表达式是合法的。"好的,那么当我解除引用一个右值时,它是一个左值吗?" 解除引用指针会得到一个左值。 - curiousguy(char*)2+2=0
不会(rvalue被视为lvalue)。 - user34537MyClass blah = MyClass( 3 ); // temporary likely to be optimized out
或者
return MyClass( 3 ); // likely to directly initialize object in caller's frame
编辑:至于这些情况中是否存在任何临时对象的问题,§12.8/15提到:
通过直接将临时对象构造到省略的复制目标中,可以省略复制操作
这表明可能存在一个与lvalue相同的临时对象。
a[10]
不是本地类型,而是用户定义的操作(a
的类型是一个类,使得定义了operator[](int)
,例如vector),只有当它返回一个引用(所有引用都是lvalue-s)时,该操作才会编译(假设对于a[10]
的类型定义了一些operator=(int)
)。 - David Rodríguez - dribeasa.operator[](int)
并返回对现有对象的引用。不一定会创建任何内容。在map::operator[]
的情况下,将创建永久对象,而不是临时对象。在vector<bool>::operator[]
的情况下,将创建一个具有定义operator=
的临时对象,但它在技术上不是左值。 - Potatoswatterconst float
类型的临时对象上:int i;
const float &cfr = i;
这个行为就像是“好像”:
int i;
const float __tmp_cfr = i; // introduced by the compiler
const float &cfr = __tmp_cfr;
i
提升为float
的表达式)是一个右值。我希望有人能比我更好地解释这个问题。 - Xeofloat(i)
确实是一个rvalue,它被绑定到常量引用上。一个假设的表达式 cfr
将是一个 lvalue。 - Kerrek SB