Подтвердить что ты не робот

Построение объекта const

С++ 11 §12.1/14:

Во время построения объекта const, если к объекту или любому из его подобъектов обращается доступ через значение l, которое равно не получено, прямо или косвенно, от конструкторов этого указатель, то полученное таким образом значение объекта или подобъекта неопределенные. [Пример:

struct C;
void no_opt(C*);

struct C {
    int c;
    C() : c(0) { no_opt(this); }
};

const C cobj;

void no_opt(C* cptr) {
    // value of cobj.c is unspecified
    int i = cobj.c * 100;
    cptr->c = 1;
    // value of cobj.c is unspecified
    cout << cobj.c * 100 << '\n';
}

Сопоставление приведенных выше выходов 100. Мой вопрос в том, почему значение cobj.c должно быть неуказано, когда список инициализации устанавливает его на 0 перед вводом конструктора? Как это поведение отличается в случае, если используется неконстантный объект?

4b9b3361

Ответ 1

Genuinely const объекты могут обрабатываться компилятором как законные константы. Он может считать, что их значения никогда не изменяются или даже не хранятся в памяти const, например. ROM или Flash. Таким образом, вам нужно использовать неконстантный путь доступа, предоставляемый this, если объект, по сути, не является константой. Это условие существует только при строительстве объекта и его разрушении.

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

Как упоминает Маттиу, это сильный "запах кода" для доступа к объекту помимо this во время строительства или уничтожения. Рассматривая С++ 11 §3.8 [basic.life] ¶1 и 6, казалось бы, что cobj.c внутри конструктора UB по той же причине находится внутри деструктора, независимо от того, что объект const или §12.1/14, поскольку его время жизни не начинается до завершения инициализации (возвращается конструктор).

Возможно, это сработает, но оно вызовет тревоги для хороших программистов на C++, а по книге это незаконно.

Ответ 2

Причина цитируемого правила заключается в том, чтобы разрешить компилятору оптимизация на основе константы объекта. Например, в зависимости от оптимизации ваш компилятор может заменить второй cobj.c * 100 в no_opt с i. Скорее, в этом конкретном случае оптимизатор будет подавлять i и его инициализацию полностью, поэтому код будет работать. Но это может быть не если вы также выведете i, перед изменением cptr->c; Все это зависит о том, насколько агрессивен компилятор оптимизирует. Но компилятору разрешено предположим, что *cptr не является псевдонимом для cobj, потому что cobj является const, где при изменении *cptr, поэтому он не может указывать на объект const без поведения undefined.

Если объект не const, конечно, проблема не возникает; компилятор должен всегда учитывать возможное наложение *cptr и cobj.