过去几年我一直在使用 C++ 进行编码。但是有一个问题我还没有弄清楚。我想问一下,C++ 中的所有临时变量都是右值吗?
如果不是,谁能给我一个例子,其中代码中临时生成的是左值?
过去几年我一直在使用 C++ 进行编码。但是有一个问题我还没有弄清楚。我想问一下,C++ 中的所有临时变量都是右值吗?
如果不是,谁能给我一个例子,其中代码中临时生成的是左值?
不。
C++ 语言规范从未像您所询问的那样做出如此直接的断言。语言标准中的任何地方都没有说“所有临时对象都是右值”。此外,这个问题本身有点用词不当,因为在 C++ 语言中作为右值的属性不是对象的属性,而是表达式的属性(即其结果的属性)。这实际上是它在语言规范中的定义:对于不同类型的表达式,它表示结果何时是左值,何时是右值。除其他外,这实际上意味着临时对象可以作为右值和左值访问,具体取决于用于执行访问的特定表达式形式。
例如,文字2 + 3
表达式的结果显然是一个右值,一个临时的类型int
。我们不能将一元&
应用于它,因为一元&
需要一个左值作为它的操作数
&(2 + 3); // ERROR, lvalue required
但是,众所周知,常量引用可以附加到临时对象,如
const int &ri = 2 + 3;
在这种情况下,引用附加到临时,延长后者的生命周期。显然,一旦完成,我们就可以访问与左值相同的临时值ri
,因为引用始终是左值。例如,我们可以轻松合法地将一元&
应用于引用并获得指向临时的指针
const int *pi = &ri;
只要临时仍然存在,该指针就保持完全有效。
另一个对临时对象进行左值访问的明显例子是当我们通过this
指针访问类类型的临时对象时。的结果*this
是一个左值(一元的结果应用于数据指针的情况总是如此*
),但它并没有改变实际对象可能很容易成为临时对象的事实。对于给定的类 type T
, expressionT()
是一个右值,正如语言标准中明确规定的那样,但是通过*T().get_this()
expression 访问的临时对象(具有明显的实现T::get_this()
)是一个左值。与前面的示例不同,此方法允许您立即获得一个非 const 限定的左值,该左值引用一个临时对象。
因此,同样的临时对象可能很容易被“视为”为右值或左值,具体取决于您用于“查看”该对象的表达式(什么样的访问路径)。
Prasoon Saurav 已经链接了一个非常好的 clc++ 线程。在那里,James Kanze 解释了为什么这个问题没有真正的意义。它归结为:
因此,这个问题没有意义。
一个很好的例子是下面的代码:
int main() {
const int& ri = 4;
std::cout << ri << std::endl;
}
带值的临时 int4
不是表达式。打印的表达式ri
不是临时的。它是一个左值,指的是一个临时值。
好吧,那个数组运算符返回一个引用,任何返回引用的函数都可以被认为做同样的事情吗?所有引用都是 const,虽然它们可以是左值,但它们会修改引用的内容,而不是引用本身。操作员也是如此*
,
*(a temp pointer) = val;
我发誓我曾经使用过一些编译器,它会将临时值传递给任何需要引用的函数,
所以你可以去:
int Afunc()
{
return 5;
}
int anotherFunc(int & b)
{
b = 34;
}
anotherFunc(Afunc());
但是现在找不到可以让你这样做的,引用必须是 const 才能允许传递临时值。
int anotherFunc(const int & b);
无论如何,引用可以是左值和临时的,诀窍是它自己的引用不会被修改,只有它引用的内容。
如果将->
运算符算作运算符,则临时指针可以是左值,但同样的条件适用,它不是要更改的临时指针,而是它指向的东西。
这取决于您认为临时变量是什么。你可以写类似
#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 中运行。您可以在键盘中运行代码
我认为 (c+2+4) 是一个右值,尽管我想分配给它。当我取消引用它时,它将成为一个左值。所以是的,所有临时变量都是右值。但是您可以通过取消引用将右值(因此是临时的)变成左值
数组索引操作既是临时的又是左值,例如 a[10] = 1 就是您要查找的示例;左值是一个临时的计算指针。
简短的回答:是的,但我不会引用标准,因为证明这一点需要解决各种临时问题。根据定义,临时语句的生命周期只有一个语句,因此将事物分配给一个语句充其量是一种糟糕的风格。
有趣的答案:复制省略可以制作(通常制作)与左值对象相同的临时对象。例如,
MyClass blah = MyClass( 3 ); // temporary likely to be optimized out
或者
return MyClass( 3 ); // likely to directly initialize object in caller's frame
编辑:至于在这些情况下是否有任何临时对象的问题,§12.8/15 提到
可以通过将临时对象直接构造到省略复制的目标中来省略复制操作
这表明存在一个可能与左值相同的临时对象。
如果不是,谁能给我一个例子,其中代码中的临时生成是左值?
以下代码将常量引用绑定到const float
编译器创建的临时类型对象:
int i;
const float &cfr = i;
行为是“好像”:
int i;
const float __tmp_cfr = i; // introduced by the compiler
const float &cfr = __tmp_cfr;