[英]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 个地址总是彼此不同。
这是一个可以玩的演示。
那么在每种情况下f
返回的规则是什么?
C++ 中的两个活动对象(几乎)总是具有不同的地址。
由于#1 #2 中的临时对象具有不重叠的生命周期,因此编译器可以自由地将#1 的存储重用于#2。
但是在#3 中,所有的临时对象在表达式结束之前都是活动的(出于显而易见的原因),在这种情况下,它们必须具有不同的地址。
除了“as if”规则之外,C++ 不支持对相同子表达式的保证缓存。 这意味着如果您不获取地址,编译器完全合理地存储它们,但它喜欢或根本不存储它们。
N4861 Draft C++20 [6.7.9.2]除非 object 是位字段或大小为零的子对象,否则 object 的地址是它占用的第一个字节的地址。 如果一个对象嵌套在另一个对象中,或者如果至少一个是大小为零的子对象并且它们属于不同类型,则两个具有重叠生命周期且不是位域的对象可能具有相同的地址;否则,它们具有不同的地址并占用不相交的存储字节。 ^28
在您的情况下,例外情况不适用。 脚注 ^28 也准确地说明了我上面写的内容:
^28 :在“as-if”规则下,如果程序无法观察到差异,则允许实现将两个对象存储在同一机器地址,或者根本不存储 object。
来自@RiaD 的好问题:
但是这两个 42 必须是不同的对象吗? 例如,“abc”和“abc”可以是同一个数组。
行为取决于使用的文字类型,并在N4861 Draft C++20 5.13 [lex.literal]中精确定义。
字符串文字是所有文字类型中的一个例外,因为它们被归类为左值,因此具有地址。
[lex.string.14]评估字符串文字会产生字符串文字 object 和 static 存储持续时间,从上面指定的给定字符初始化。 所有字符串文字是否不同(即存储在不重叠的对象中)以及字符串文字的连续评估是否产生相同或不同的 object 未指定。
这意味着文字可能具有与观察到的@RiaD 相同的地址,但这与上述内容并不矛盾,因为它们是相同的 object。
所有其他文字,包括整数,都是纯右值表达式,它们不是对象(从某种意义上说它们没有地址),但在某些情况下,它们会通过foo(42)
发生的临时实现产生临时 object 因为它是绑定的到一个const T&
。 AFAIK标准没有明确说相同的两个纯右值表达式必须产生不同的临时,但它说一个表达式初始化一个临时,所以我相信每个表达式都必须创建一个新的临时,生命周期也略有不同。 因此,两个地址(如果观察到)必须不同。
临时性一直持续到导致它们 spring 生命的完整表达式结束。
[类.临时]
4 ... 临时对象在评估完整表达式 ([intro.execution]) 的最后一步被销毁,该完整表达式 ([intro.execution]) (词法上)包含它们被创建的点。
这适用于所有临时工。 这意味着在表达式 #3 中,假设它的评估结束时没有抛出异常,两个临时对象的生命周期可能重叠。
除了少数例外(此处均不适用),两个不同的对象在其生命周期内将具有不同的地址。
我以前的一些评论应要求在这里重新发布:
真正有趣的是 C++ 不要求对 object 的地址进行具体编码。 (它没有提到 function 的地址,顺便说一句。)这是很自然的,因为 C++ 抽象机在大多数情况下对地址不感兴趣。
两个不同的对象只是......不是同一个对象,因为它们具有不同的身份。 恒等的概念肯定被更广泛地使用,例如左值,尽管它在大多数情况下也被小心地回避了。 如果对身份差异感兴趣,规范只是规定了访问对象的唯一允许方式(例如严格的别名规则),因为这里有多少对象被认为是实现细节。 地址在概念上是从对象的身份派生的,它不能帮助您使差异更加明显。
依靠地址的概念来描述不同对象(及其子对象)之间的布局是合适的。 在这种特殊情况下,身份是不够的。 这不是这里的情况(重叠的生命周期,而不是重叠的存储)。 当涉及地址时,推理变得混乱。 正如所回答的那样,由于 as-if 规则是有效的,当没有可移植的方式来区分地址时,不同的对象可以具有相同的地址。 另请注意, addressof
和[[no_unique_address]]
并不真正需要区分地址(而只是身份)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.