14

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

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;
}

这里接受的答案提到

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

根据该声明,可以得出结论,临时存在于某个内存位置(因此其地址可能已被占用)并且编译器决定通过在存在临时的相同位置创建就地对象来消除临时对象.

这是否与不能获取临时地址的事实相矛盾?

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

4

7 回答 7

15
&std::string("Hello World")

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

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

42

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

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

于 2010-11-29T05:56:43.343 回答
7

长答案:

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

根据定义:

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

因此,不需要非常详尽的证据来证明临时人员有地址。这是根据定义。

OTOH,您不仅在获取地址,而且还在使用内置的 address-of 运算符。内置地址操作符的规范说你必须有一个左值

  • &std::string()格式不正确,因为std::string()rvalue。在运行时,此表达式的此评估会创建一个临时对象作为副作用,并且该表达式会产生一个引用所创建对象的右值。
  • &(std::string() = "Hello World")是良构的,因为std::string() = "Hello World"是一个左值。根据定义,左值指的是一个对象。这个左值引用的对象是完全相同的临时对象

简短的回答:

这是规则。它不需要某些人编造的(不正确的、不合理的)理由。

于 2011-12-05T09:11:07.080 回答
4

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

诸如此类的表达

99

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

都是右值。

$3.10/2 - “一个左值指的是一个对象或函数。一些右值表达式——那些类或 cv 限定的类类型——也指对象。47)”

这是我的猜测:尽管 Rvalues 可能会占用存储空间(例如在对象的情况下),但 C++ 标准不允许使用它们的地址来保持与内置类型的一致性

不过,这里有一些有趣的东西:

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

int main(){
   f(42);
}

表达式“42”是一个右值,它绑定到“对 const double 的引用”,因此它创建了一个 double 类型的临时对象。这个临时的地址可以在函数'f'中获取。但请注意,在“f”内部,这并不是真正的临时值或右值。当它被命名为“dbl”时,它被视为“f”内的左值表达式。

这是NRVO 上的内容(类似)

于 2010-11-29T06:10:03.970 回答
3

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

你“做某事”的语言要求有点武断,但它使 C++ 程序更干净。如果人们养成记录临时地址的习惯,那就糟透了。地址的概念与生命周期的概念密切相关,因此使“瞬时”值缺少地址是有意义的。不过,如果您小心,您可以获取一个地址并在标准允许的生命周期内使用它。

这里的其他答案存在一些谬误:

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

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

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

    值得一提的是,C++0x 通过将纯右值定义为表达式的值,独立于存储,将左值定义为独立于其内容的存储位置,从而清理这些概念。这可能是 C++03 标准的初衷。

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

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

    这是转换数字并检查转换是否成功的一个很好的习惯用法。它涉及创建一个临时对象,在其上调用一个变异函数,然后将其销毁。远非毫无意义。

于 2010-11-29T06:34:45.203 回答
2

可以采用,但是一旦临时不再存在,您就会留下一个悬空指针。

编辑

对于downvoters:

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

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

编辑2

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

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

于 2010-11-29T05:57:01.243 回答
0

一个原因是您的示例将授予该方法对临时的写访问权限,这是没有意义的。

您提供的引用与这种情况无关,它是带有初始化程序的声明器中允许的特定优化。

于 2010-11-29T06:15:47.880 回答
-1

为什么占用临时地址是非法的?

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

于 2017-02-05T21:03:23.267 回答