两个临时对象的地址是否保证在同一表达式中不同?

Are the addresses of two temporaries guaranteed to be different in the same expression?

考虑以下程序:

#include <iostream>

int const * f(int const &i) 
{ 
  return &i; 
}

int main() 
{
  std::cout << f(42);  // #1
  std::cout << f(42);  // #2

  std::cout << f(42) << f(42);  // #3
}

根据编译器和设置的优化级别,打印在行 #1#2 上的地址可能不同也可能不同。

然而,无论选择何种编译器或优化级别,第 #3 行打印的 2 个地址总是彼此不同。

这里有一个 demo 可以玩。

那么在每种情况下 f returns 的规则是什么?

C++ 中的两个活动对象(几乎)总是有不同的地址。

由于 #1 #2 中的临时对象具有不重叠的生命周期,编译器可以自由地为 #2 重用 #1 的存储。

但是在#3 中,所有临时对象都处于活动状态,直到表达式结束(出于明显的原因),在这种情况下,它们必须具有不同的地址。

除了“as if”规则外,C++ 不支持保证缓存相同的子表达式。这意味着如果您不获取地址,编译器可以根据自己的喜好存储它们或根本不存储它们是完全合理的。

参考

N4861 Draft C++20 [6.7.9.2] Unless an object is a bit-field or a subobject of zero size, the address of that object is the address of the first byte it occupies. Two objects with overlapping lifetimes that are not bit-fields may have the same address if one is nested within the other, or if at least one is a subobject of zero size and they are of different types;otherwise, they have distinct addresses and occupy disjoint bytes of storage. ^28

对于您的情况,例外情况不适用。脚注^28也正是我上面写的:

^28: Under the “as-if” rule an implementation is allowed to store two objects at the same machine address or not store an object at all if the program cannot observe the difference.

编辑

来自@RiaD 的优秀问题:

But do these two 42s have to be different objects? For example, "abc" and "abc" can be the same array.

行为取决于所使用的文字类型,并在 N4861 Draft C++20 5.13 [lex.literal] 中精确定义。

  1. 字符串文字是所有文字类型中的一个例外,因为它们被归类为左值,因此具有地址。

    [lex.string.14] Evaluating a string-literal results in a string literal object with static storage duration, initialized from the given characters as specified above. Whether all string-literals are distinct (that is, are stored in nonoverlapping objects) and whether successive evaluations of a string-literal yield the same or a different object is unspecified.

    意思是文字可能与@RiaD 观察到的地址相同,但这与上述并不矛盾,因为它们是同一个对象。

  2. 所有其他文字,包括整数,都是纯右值表达式,它们不是对象(从某种意义上说,它们没有地址),但在某些情况下,它们会通过 [=41 产生一个临时对象=]临时实体化 发生在 foo(42) 上,因为它绑定到 const T&。 AFAIK 标准没有明确表示相同的两个 prvalue 表达式必须产生不同的临时值,但它说一个表达式初始化一个临时值,所以我相信每个表达式都必须创建一个新的临时值,生命周期也略有不同。因此,两个地址(如果观察到)必须不同。

临时对象会持续存在,直到导致它们 spring 复活的完整表达式结束。

[class.temporary]

4 ... Temporary objects are destroyed as the last step in evaluating the full-expression ([intro.execution]) that (lexically) contains the point where they were created.

所有临时工都是如此。这意味着在表达式 #3 中,假设其评估结束时没有抛出异常,则两个临时对象的生命周期可能重叠。

除了少数例外(none 其中适用于此处),两个不同的对象在其生命周期内将具有不同的地址。

我之前的一些评论应要求重新发布在这里:

真正有趣的是,C++ 不强制对对象地址进行具体编码。 (顺便说一句,它没有提到函数的地址。)这是很自然的,因为在大多数情况下,C++ 抽象机对地址不感兴趣。

两个不同的对象只是...不是同一个对象,因为它们具有不同的身份。身份的概念当然使用得更广泛,例如对于左值,尽管在大多数情况下它也被小心地避开了。如果对身份的差异感兴趣,规范只规定对对象的 访问 的唯一允许方式(例如严格的别名规则),因为这里有多少对象被认为是一个实现细节。地址在概念上是从对象的身份派生出来的,它无法帮助您使差异更加明显。

用地址的概念来描述不同对象(及其子对象)之间的布局是恰当的。在这种特定情况下,身份是不够的。这里不是这种情况(重叠生命周期,而不是重叠存储)。当涉及地址时,推理变得混乱。正如所回答的那样,由于 as-if 规则是有效的,因此当没有可移植的方法来区分地址时,不同的对象可以具有相同的地址。另请注意,addressof[[no_unique_address]] 并不真正需要区分地址(而只是身份)。