C++ 具有常量成员的新类的放置和分配

C++ 具有常量成员的新类的放置和分配,c++,constants,language-lawyer,assignment-operator,placement-new,C++,Constants,Language Lawyer,Assignment Operator,Placement New,为什么会有这种未定义的行为 struct s { const int id; // <-- const member s(int id): id(id) {} s& operator =(const s& m) { return *new(this) s(m); // <-- undefined behavior? } }; 结构 { const int id;//显示的代码片段没有任何内在

为什么会有这种未定义的行为

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

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

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

const int id;//显示的代码片段没有任何内在的UB。但是,几乎可以肯定,在任何正常使用情况下,UB都会立即出现

来自(我的)

如果在对象的生命周期结束后,在重用或释放对象占用的存储之前,在原始对象占用的存储位置创建了新对象,则指向原始对象的指针、引用原始对象的引用或原始对象的名称将自动执行引用新对象,并且在新对象的生命周期开始后,可用于操纵新对象,如果:

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

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

  • 原始对象的类型不是const-qualified,如果类类型不包含任何类型为const-qualified的非静态数据成员或引用类型,以及

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

由于
s
中有一个
const
成员,因此在调用
operator=
后使用原始变量将是无效的

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

const int id;
id
的值永远不会改变。然后你改变了它?@Boperson:另一种观点是我在同一位置创建了一个新对象。我清楚地记得这是合法的。@Boperson
const
只适用于对象的生存期。@YSC析构函数没有被调用不是未定义的行为。在无效对象上调用析构函数是。@YSC如果析构函数很琐碎(如本例),则不调用它是合法的。因此,我是否正确理解在C++17之前没有合法的方法访问成员?@DaBler技术上是这样的。但我强烈建议不要这样做。
auto&ref=(var=s{420})
。然后使用
ref
好的,我明白了。谢谢。@DaBler严格地说,在std上的阅读下,在常见的实现中,使用指针对象总是保证在常量改变技巧之后工作。仍然是,只要指针只包含地址(数字)然而,这是迂腐的,当然不是有意的,而那些致力于解释std的人也不会支持它。没有一个编译器会不遗余力地支持迂腐的阅读。