介绍:第一个问题是使用价值是否合法p
at all.
After a
已被摧毁,p
获得所谓的无效的指针值。引用自N4430(有关 N4430 状态的讨论,请参阅下面的“注释”):
当到达存储区域的持续时间结束时,表示已释放存储的任何部分的地址的所有指针的值变为无效的指针值.
N4430 的同一部分也介绍了使用无效指针值时的行为(几乎相同的文本出现在 C++14 [basic.stc.dynamic.deallocation]/4 中):
通过无效指针值进行间接寻址以及将无效指针值传递给释放函数具有未定义的行为。对无效指针值的任何其他使用实现定义的行为.
[ 脚注:某些实现可能会定义复制无效的指针值会导致系统生成的运行时错误。 ——脚注结束]
因此,您需要查阅实现的文档以了解此处应该发生什么(自 C++14 起)。
期限use在上面的引用中means需要左值到右值的转换,如 C++14 [conv.lval/2] 中所示:
当左值到右值转换应用于表达式 e,并且 [...] 左值引用的对象包含无效指针值时,该行为是实现定义的。
History:在 C++11 中这样说不明确的而不是实现定义的;它被改变了DR1438。请参阅这篇文章的编辑历史以获取完整的引用。
申请到p == q
:假设我们在 C++14+N4430 中接受了评估的结果p
and q
是实现定义的,并且实现没有定义硬件陷阱的发生; [expr.eq]/2 说:
如果两个指针都为空、都指向同一函数或都表示相同的地址 (3.9.2),则它们比较相等,否则它们比较不相等。
因为它是实现定义的,所以什么时候会获得什么值p
and q
进行评估后,我们无法确定这里会发生什么。但它必须是实现定义的或未指定的。
在这种情况下,g++ 似乎表现出未指定的行为;取决于-O
切换我可以让它说1
or 0
,对应于是否重复使用相同的内存地址b
after a
已被摧毁。
关于 N4430 的注意事项:这是针对 C++14 的缺陷解决方案的提议,尚未被接受。它清理了围绕对象生命周期、无效指针、子对象、联合和数组边界访问的大量措辞。
在 C++14 文本中,在 [basic.stc.dynamic.deallocation]/4 及后续段落下定义无效的指针值出现时delete
用来。然而,没有明确说明相同的原理是否适用于静态或自动存储。
[basic.compound]/3 中有一个“有效指针”的定义,但它太模糊了,无法明智地使用。[basic.life]/5(脚注)引用相同的文本来定义指向对象的指针的行为静态存储持续时间,这表明它适用于所有类型的存储。
在 N4430 中,文本从该部分向上移动了一级,以便它明确适用于所有存储持续时间。附有注释:
起草注意事项:这应该适用于所有可以结束的存储持续时间,而不仅仅是动态存储持续时间。在支持线程或分段堆栈的实现上,线程和自动存储的行为方式可能与动态存储相同。
我的想法:除了说之外,我看不到任何一致的方式来解释该标准(N4430 之前的版本)p
获取无效的指针值。除了我们已经看过的内容之外,该行为似乎没有被任何其他部分所涵盖。因此,我很高兴将 N4430 措辞视为代表本例中标准的意图。