Размещение нового и назначение класса с константным членом

Почему такое неопределенное поведение?

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

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

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

(Цитата из стандарта была бы хороша).

Этот вопрос возник из этого ответа.


person DaBler    schedule 24.11.2017    source источник
comment
const int id; говорит, что значение id никогда не изменится. А потом поменяете?   -  person Bo Persson    schedule 24.11.2017
comment
Странно хотеть изменить что-то, что вы намеренно сделали imизменяемым.   -  person molbdnilo    schedule 24.11.2017
comment
@BoPersson: Другая точка зрения заключается в том, что я создаю новый объект в том же месте.   -  person DaBler    schedule 24.11.2017
comment
Я читаю стандарт С++ 17 уже 30 минут и до сих пор не могу найти набор правил, запрещающих двойную конструкцию объекта.   -  person YSC    schedule 24.11.2017
comment
Я отчетливо помню, что это было законно. @BoPersson const применяется только к сроку службы объекта.   -  person Passer By    schedule 24.11.2017
comment
@PasserBy разве это не законно, только если вы каким-то образом вызываете деструктор объекта между двумя вызовами его конструктора? Я действительно не нашел упоминания об этом в стандарте. Он должен быть где-то спрятан.   -  person YSC    schedule 24.11.2017
comment
@YSC Деструкторы, которые не вызываются, не являются неопределенным поведением. Вызов деструктора для недопустимого объекта.   -  person Passer By    schedule 24.11.2017
comment
@YSC Если деструктор тривиален (как в этом случае), то его можно не вызывать. timsong-cpp.github.io/cppwp/basic.life# 5.предложение-1   -  person Rakete1111    schedule 24.11.2017
comment
@ Rakete1111 это ответ!   -  person YSC    schedule 24.11.2017
comment
@YSC Я так не думаю. В нем говорится, что UB не возникнет из-за того, что деструкторы не вызываются, что в лучшем случае очень частично.   -  person Passer By    schedule 24.11.2017
comment
@PasserПо чему? Я создал чат, видимо, где-то недопонимание   -  person YSC    schedule 24.11.2017
comment
@YSC Я не знаю, прав ли я на самом деле, если это применимо, даже если объект внутри объекта равен const, потому что я не знаю, timsong-cpp.github.io/cppwp/basic.life#10 кажется применимым.   -  person Rakete1111    schedule 24.11.2017
comment
@Rakete1111 [basic.life]/10 касается const объектов, а не объектов с const элементами, не так ли?   -  person YSC    schedule 24.11.2017
comment
@YSC В общем-то, я не знаю :( Хотя это похоже на первое   -  person Rakete1111    schedule 24.11.2017
comment
Читая [basic.life]/9, он явно говорит, что все в порядке?   -  person YSC    schedule 24.11.2017
comment
@YSC Константный член - это константный объект, не так ли?   -  person curiousguy    schedule 07.07.2018


Ответы (1)


Нет ничего, что делало бы показанный фрагмент кода по своей сути UB. Однако почти наверняка UB последует сразу же при любом обычном использовании.

Из [basic.life]/8 (выделено мной)

Если после того, как время жизни объекта закончилось и до того, как хранилище, которое занимал объект, было повторно использовано или освобождено, новый объект создается в месте хранения, которое занимал исходный объект, указатель, указывающий на исходный объект, ссылка, которая относится к исходному объекту, или имя исходного объекта будет автоматически ссылаться на новый объект и, как только начнется время жизни нового объекта, может использоваться для управления новым объектом, если:

  • хранилище для нового объекта точно перекрывает место хранения, которое занимал исходный объект, и

  • новый объект имеет тот же тип, что и исходный объект (игнорируя cv-квалификаторы верхнего уровня), и

  • тип исходного объекта не является константным и, если это тип класса, не содержит нестатических членов данных, тип которых является константным или ссылочным типом, и

  • исходный объект был наиболее производным объектом типа 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
person Passer By    schedule 24.11.2017
comment
Итак, правильно ли я понимаю, что нет законного способа получить доступ к членам до С++ 17? - person DaBler; 24.11.2017
comment
@DaBler Технически есть. Но я настоятельно рекомендую против этого. auto& ref = (var = s{420});. А затем используйте ref - person Passer By; 24.11.2017
comment
Хорошо, я понимаю. Спасибо. - person DaBler; 24.11.2017
comment
@DaBler Технически, при строгом чтении стандартного стандарта в распространенных реализациях использование объекта-указателя всегда гарантированно работало после этого трюка с изменением константы. Еще есть, пока указатель содержит только адрес (число). Однако это педантично и, конечно же, НЕ предназначено, и люди, чья работа заключается в интерпретации стандартного стандарта, не поддержат его. Ни один компилятор не будет изо всех сил поддерживать такое педантичное прочтение. - person curiousguy; 07.07.2018