【问题标题】:Placement new and assignment of class with const member使用 const 成员放置新的和分配类
【发布时间】:2018-05-08 11:44:35
【问题描述】:

为什么这是未定义的行为?

struct s
{
    const int id; // <-- const member

    s(int id):
        id(id)
    {}

    s& operator =(const s& m) {
        return *new(this) s(m); // <-- undefined behavior?
    }
};

(引用标准会很好)。

这个问题来自this answer

【问题讨论】:

  • const int id; 表示id 的值永远不会改变。然后你改变它?
  • @BoPersson:另一种观点是我在同一个位置创建了一个新对象。
  • 我清楚地记得这是合法的。 @BoPersson const 仅适用于对象的生命周期。
  • @YSC 未调用析构函数不是未定义的行为。对无效对象调用析构函数是。
  • @YSC 如果析构函数是微不足道的(就像在这种情况下),那么不调用它是合法的。 timsong-cpp.github.io/cppwp/basic.life#5.sentence-1

标签: c++ constants language-lawyer assignment-operator placement-new


【解决方案1】:

没有什么可以使显示的代码 sn-p 本质上是 UB。但是,几乎可以肯定,在任何正常使用情况下,UB 都会立即跟进。

来自[basic.life]/8(强调我的)

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

  • 新对象的存储空间正好覆盖了原始对象占用的存储位置,并且

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

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

  • 原始对象是T 类型的最衍生对象,而新对象是T 类型的最衍生对象(也就是说,它们不是基类子对象)。

由于s 中有一个const 成员,在调用operator= 后使用原始变量将是UB。

s var{42};
var = s{420};         // OK
do_something(var.id); // UB! Reuses s through original name
do_something(std::launder(&var)->id);  // OK, this is what launder is used for

【讨论】:

  • 那么,我是否正确理解在 C++17 之前没有合法的方式访问成员?
  • @DaBler 从技术上讲,有。但我强烈建议不要这样做。 auto&amp; ref = (var = s{420});。然后使用ref
  • 好的,我明白了。谢谢。
  • @DaBler 从技术上讲,在标准的严格阅读下,在常见的实现上,使用指针对象总是保证在改变 const 技巧之后工作。仍然是,只要一个指针只包含一个地址(数字)。但是,这是迂腐的,当然不是有意的,工作是解释 std 的人不会支持它。没有编译器会竭尽全力支持这种迂腐的阅读。
  • 这个答案可能不再正确,因为引用的句子在 C++20 中被放宽了:github.com/cplusplus/draft/commit/…
猜你喜欢
  • 2012-07-21
  • 2020-07-04
  • 2020-11-21
  • 2017-01-12
  • 1970-01-01
  • 2016-05-01
  • 2020-09-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多