2012-01-05 2 views
13

C++ 11 §12.1/14:Строительства константного объекта

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

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

+0

Я хотел бы отметить, что в примере выше 'cobj.c', до того, как 'cobj' будет полностью построен. Мне кажется, что даже для неконстантных объектов это действие сомнительно. –

+0

@ MatthieuM. Зачем? Выглядит на мой взгляд (даже если вы добавите базовый класс в структуру C). –

+0

@VJovic: до тех пор, пока конструктор не запустился, объект еще не оживлен - см. Вопрос Саттера в [Ошибки конструктора (или объекты, которые никогда не были)] (http://www.gotw.ca/publications /mill13.htm). Если объект еще не жив, доступ к нему сомнительный. Я не говорю, что это обязательно неопределенный или неопределенный, просто он «плохо пахнет». –

ответ

6

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

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

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

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

+0

'C(): c (0) {c = 1; } '- так что вы говорите, что это также недействительно? –

+0

Это выглядит не по теме: оно не учитывает тот факт, что 'cobj.c' имеет неопределенное значение во время запуска конструктора' C' (даже после запуска списка инициализации). Он также не учитывает, почему поведение было бы различным (в этом отношении) между объектами const и non-const. –

+0

@ MatthieuM. Я думаю, что рассмотрел эти проблемы. Проблема в том, что компилятор может распознать путь доступа cobj.c' как имеющий значение, которое не может измениться. Независимо от того, какая функция обращается к нему, объект 'const', который не имеет постоянного значения, находится вне пределов. – Potatoswatter

3

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

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

Смежные вопросы