34

过去几年我一直在使用 C++ 进行编码。但是有一个问题我还没有弄清楚。我想问一下,C++ 中的所有临时变量都是右值吗?

如果不是,谁能给我一个例子,其中代码中临时生成的是左值

4

7 回答 7

46

不。

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 限定的左值,该左值引用一个临时对象。

因此,同样的临时对象可能很容易被“视为”为右值或左值,具体取决于您用于“查看”该对象的表达式(什么样的访问路径)。

于 2010-01-27T09:51:18.837 回答
10

Prasoon Saurav 已经链接了一个非常好的 clc++ 线程。在那里,James Kanze 解释了为什么这个问题没有真正的意义。它归结为:

  • rvalue-ness 是表达式的(布尔)属性 - 每个表达式都是左值或右值
  • 临时变量不是表达式

因此,这个问题没有意义。

一个很好的例子是下面的代码:

int main() {
  const int& ri = 4;
  std::cout << ri << std::endl; 
}

带值的临时 int4不是表达式。打印的表达式ri不是临时的。它是一个左值,指的是一个临时值。

于 2010-01-27T08:51:04.973 回答
1

好吧,那个数组运算符返回一个引用,任何返回引用的函数都可以被认为做同样的事情吗?所有引用都是 const,虽然它们可以是左值,但它们会修改引用的内容,而不是引用本身。操作员也是如此*

*(a temp pointer) = val;

我发誓我曾经使用过一些编译器,它会将临​​时值传递给任何需要引用的函数,

所以你可以去:

int Afunc()
{
   return 5;
}

int anotherFunc(int & b)
{
    b = 34;
}


anotherFunc(Afunc());

但是现在找不到可以让你这样做的,引用必须是 const 才能允许传递临时值。

int anotherFunc(const int & b);

无论如何,引用可以是左值和临时的,诀窍是它自己的引用不会被修改,只有它引用的内容。

如果将->运算符算作运算符,则临时指针可以是左值,但同样的条件适用,它不是要更改的临时指针,而是它指向的东西。

于 2010-01-27T07:32:35.207 回答
0

这取决于您认为临时变量是什么。你可以写类似

#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) 是一个右值,尽管我想分配给它。当我取消引用它时,它将成为一个左值。所以是的,所有临时变量都是右值。但是您可以通过取消引用将右值(因此是临时的)变成左值

于 2010-01-27T07:50:52.337 回答
0

数组索引操作既是临时的又是左值,例如 a[10] = 1 就是您要查找的示例;左值是一个临时的计算指针。

于 2010-01-27T07:20:11.737 回答
0

简短的回答:是的,但我不会引用标准,因为证明这一点需要解决各种临时问题。根据定义,临时语句的生命周期只有一个语句,因此将事物分配给一个语句充其量是一种糟糕的风格。

有趣的答案:复制省略可以制作(通常制作)与左值对象相同的临时对象。例如,

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 提到

可以通过将临时对象直接构造到省略复制的目标中来省略复制操作

这表明存在一个可能与左值相同的临时对象。

于 2010-01-27T08:42:48.090 回答
-2

如果不是,谁能给我一个例子,其中代码中的临时生成是左值?

以下代码将常量引用绑定到const float编译器创建的临时类型对象:

int i;
const float &cfr = i;

行为是“好像”:

int i;
const float __tmp_cfr = i; // introduced by the compiler
const float &cfr = __tmp_cfr;
于 2011-12-07T09:55:27.673 回答