Строительство постоянного объекта - PullRequest
13 голосов
/ 05 января 2012

C ++ 11 §12.1 / 14:

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

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 перед вводом конструктора?Чем отличается это поведение в случае использования неконстантного объекта?

Ответы [ 2 ]

6 голосов
/ 05 января 2012

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

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

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

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

3 голосов
/ 05 января 2012

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...