为什么拿一个临时非法的地址?

时间:2010-11-29 05:53:28

标签: c++ temporary memory-address lvalue rvalue

我知道下面写的代码是非法的

void doSomething(std::string *s){}
int main()
{
     doSomething(&std::string("Hello World"));
     return 0;
}

原因是我们不允许获取临时对象的地址。但我的问题是为什么?

让我们考虑以下代码

class empty{};
int main()
{
      empty x = empty(); //most compilers would elide the temporary
      return 0;
}

接受的答案here提及

“通常编译器会将临时和副本构造为两个对象,它们位于内存的完全相同的位置,并避免复制。”

根据声明可以得出结论,临时存在于某个内存位置(因此可能已经采用了它),编译器决定通过在同一位置创建一个就地对象来消除临时对象。暂时存在。

这是否与临时地址不相符?

我还想知道如何实现返回值优化。有人可以提供与RVO实施相关的链接或文章吗?

7 个答案:

答案 0 :(得分:13)

&std::string("Hello World")

问题不在于std::string("Hello World")会产生临时对象。问题是表达式std::string("Hello World")是一个引用临时对象的右值表达式。

您不能获取右值的地址,因为并非所有的右值都有地址(并非所有的右值都是对象)。请考虑以下事项:

42

这是一个整数文字,它是一个主要表达式和一个右值。它不是一个对象,它(可能)没有地址。 &42是荒谬的。

是的,右值可以引用一个对象,就像你的第一个例子中的情况一样。问题是并非所有的右值都引用了对象。

答案 1 :(得分:5)

答案很长:

  

[...]可以得出结论,临时存在于某个存储位置

根据定义:

  • “temporary”代表:临时对象
  • 一个对象占据存储区域
  • 所有对象都有一个地址

因此,不需要非常精细的证据来证明临时具有地址。这是定义。

OTOH,您不只是获取地址,而是使用内置地址运算符。内置地址运算符的规范表明您必须具有左值

  • &std::string()格式错误,因为std::string()右值。在运行时,对此表达式的此求值将创建一个临时对象作为副作用,并且该表达式将生成一个引用所创建对象的右值。
  • &(std::string() = "Hello World")格式正确,因为std::string() = "Hello World"左值。根据定义,左值是指对象。此左值引用的对象与完全相同的临时

简答:

这是规则。它不需要某些人正在制定的(不正确的,不合理的)理由。

答案 2 :(得分:4)

$ 5.3.1 / 2 - “一元&运算符的结果是指向其操作数的指针。操作数应为左值或限定值。

表达式,如

99

A() // where A is a user defined class with an accessible 
    // and unambiguous default constructor

都是Rvalues。

  

$ 3.10 / 2 - “左值是指   对象或功能。一些右值   表达式 - 类或者的表达式   cv限定的类类型 - 也参考   objects.47)“

这是我的猜测:即使Rvalues可能占用存储空间(例如在对象的情况下),C ++标准也不允许使用其内容来保持内置类型的一致性

这里有一些有趣的事情:

void f(const double &dbl){
   cout << &dbl;
}

int main(){
   f(42);
}

表达式'42'是一个Rvalue,它绑定到'const double'的引用,因此它创建了一个double类型的临时对象。这个临时的地址可以在函数'f'中获取。但请注意,在'f'里面,这不是一个临时或Rvalue。当它被赋予诸如'dbl'之类的名称时,它被视为'f'中的左值表达式。

NRVO上的

Here's(类似的)

答案 3 :(得分:3)

临时是C ++“rvalue”的一个例子。它应该纯粹代表其类型中的值。例如,如果您在程序中的两个不同位置编写42,则42的实例无法区分,尽管可能在不同时间位于不同位置。你不能拿地址的原因是你需要做一些事情来指定应该有一个地址,因为否则地址的概念在语义上是不清洁和不直观的。

你“做某事”的语言要求有点武断,但它使C ++程序更加清晰。如果人们习惯接受临时的话,那就太糟糕了。地址的概念与生命的概念密切相关,因此使“瞬时”值缺乏地址是有意义的。不过,如果你小心,你可以获得一个地址,并在标准允许的生命周期内使用它。

其他答案中有一些谬论:

  • “你不能取rvalue的地址,因为并非所有的rvalues都有地址。” - 并非所有左值都有地址。参与简单循环并且随后未使用的类型int的典型局部变量可能被分配寄存器但没有堆栈位置。没有内存位置意味着没有地址。但是,如果您获取其地址,编译器将分配一个内存位置。 rvalues也是如此,它可能绑定到const个引用。 “42的地址”可以这样获得:

    int const *fortytwo_p = & static_cast<int const &>( 42 );
    

    当然,;之后地址无效,因为暂时性是暂时的,这可能会产生额外的指令,因为机器可能无意义地将42存储到堆栈中。

    值得一提的是,C ++ 0x通过将 prvalue 定义为表达式的值来清理概念,独立于存储, glvalue 是存储位置与其内容无关。这可能是C ++ 03标准的首要目的。

  • “然后你可以修改临时,这是没有意义的。” - 实际上有副作用的临时工具对修改很有用。考虑一下:

    if ( istringstream( "42" ) >> my_int )
    

    这是一个很好的习惯用于转换数字并检查转换是否成功。它涉及创建一个临时函数,在其上调用一个变异函数,然后销毁它。

答案 4 :(得分:2)

可以采取,但一旦暂时不存在,你就会有一个悬空指针。

修改

对于downvoters:

const std::string &s = std::string("h");
&s;

是合法的。 s是对临时的引用。因此,可以采用临时对象的地址。

<强> EDIT2

绑定引用是它们绑定的别名。因此,对临时的引用是该临时的另一个名称。因此,上段中的第二个陈述成立。

OP的问题是关于临时性(就他所使用的词而言),他的例子是关于rvalues。这是两个截然不同的概念。

答案 5 :(得分:0)

一个原因是你的例子会给方法写入临时访问权限,这是没有意义的。

您提供的引用与此情况无关,它是具有初始值设定项的声明符中允许的特定优化。

答案 6 :(得分:0)

  

为什么要取一个临时非法的地址?

临时变量的范围仅限于某些特定方法或某些块,只要方法调用返回临时变量就会从内存中删除,因此如果我们返回内存中不再存在的变量的地址它没有任何意义。地址仍然有效,但该地址现在可能包含一些垃圾值。