C++中的"重新绑定"引用是否合法?

Meh*_*dad 38 c++ reference placement-new language-lawyer

C++中的以下内容是否合法?

据我所知,Reference有一个简单的析构函数,所以它应该是合法的.
但我认为参考不能合法反弹......可以吗?

template<class T>
struct Reference
{
    T &r;
    Reference(T &r) : r(r) { }
};

int main()
{
    int x = 5, y = 6;
    Reference<int> r(x);
    new (&r) Reference<int>(y);
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*som 18

您没有重新绑定引用,您正在另一个具有新位置的内存中创建一个新对象.由于旧对象的析构函数从未运行过,我认为这将是未定义的行为.

  • 如果在重新运行对象的存储之前没有运行析构函数,则不是自动UB,只有"依赖于析构函数产生的副作用的任何程序具有未定义的行为"(ISO/IEC 14882:2011 3.8 [基本.生命]/4)这使得整个类程序可以省略析构函数的调用 - 甚至是非平凡的调用 - 并且没有UB. (5认同)
  • 你能详细说明析构函数没有被运行吗?如果它是微不足道的,它是不是真的UB不运行? (3认同)
  • 我不认为引用甚至有析构函数. (2认同)

CB *_*ley 11

您的示例中没有引用反弹.第一个引用(在第二行上使用名称构造)在其整个生命周期中r.r绑定到int表示x为.当第三行的放置新表达式重新使用其包含对象的存储时,此引用的生命周期结束.替换对象包含一个绑定y其整个生命周期的引用,该引用一直持续到其范围的结尾 - 结束main.

  • @MarkRansom:但是"尚未被破坏的部分"将是一个不正确的假设,因为将对象的存储重用于另一个对象会破坏原始对象.这意味着这种优化将是不符合的. (2认同)

dyp*_*dyp 4

我想我在“引用的”下面的一段话中找到了答案,该段谈论了琐碎的 dtor / dtor 副作用,即 [basic.life]/7:

如果一个对象的生命周期结束后,在该对象占用的存储空间被重用或释放之前,在原对象占用的存储位置上创建一个新的对象,一个指向原对象的指针,一个指向该对象的引用引用原始对象,或者原始对象的名称将自动引用新对象,并且一旦新对象的生命周期开始,可用于操作新对象,如果:

  • 新对象的存储完全覆盖原始对象占用的存储位置,并且

  • 新对象与原始对象具有相同的类型(忽略顶级 cv 限定符),并且

  • 原始对象的类型不是 const 限定的,并且如果是类类型,则不包含任何类型为 const 限定的非静态数据成员或引用类型,并且

  • 原始对象是类型的最派生对象T,新对象是类型的最派生对象T(即,它们不是基类子对象)。

通过重用存储,我们结束了原始对象的生命周期 [basic.life]/1

类型对象的生命周期T在以下情况结束:

  • 如果T是具有非平凡析构函数的类类型,则析构函数调用开始,或者

  • 对象占用的存储被重用或释放。

所以我认为 [basic.life]/7 涵盖了这种情况

Reference<int> r(x);
new (&r) Reference<int>(y);
Run Code Online (Sandbox Code Playgroud)

我们结束 表示的对象的生命周期r,并在同一位置创建一个新对象。

由于是具有引用数据成员的类类型,因此不满足Reference<int>[basic.life]/7 的要求。也就是说,甚至可能不会引用新对象,并且我们可能不会使用它来“操作”这个新创建的对象(我也将这种“操作”解释为只读访问)。r


归档时间:

查看次数:

1631 次

最近记录:

11 年,7 月 前