2012-06-25 21 views
25

Можно создать дубликат:
int var = 1; void main() { int i = i; }Почему 'int i = i;' законно?

Следующий код может пройти компиляцию под как г ++ и Visual C++. Почему это законно? Это выглядит необоснованным и может вызвать скрытые ошибки.

int main() { 
    int i = i; 
} 
+7

Для меня это не незаконно, это просто злоупотребление нотами. –

+8

Он оценивает как (i) 'int i' (ii)' i = i' в этом порядке –

+0

Я думаю по той же причине, что только 'int i;' без присвоения 'i' является законным. – asmeurer

ответ

42

EDIT: Это синтаксически правовой, но приводит к непредсказуемому поведению, если вы используете x.

Это не, поскольку вы назначаете неинициализированную переменную другой (ну, той же) неинициализированной переменной. Просто потому, что он компилируется, это не значит, что это законно. Это допустимый синтаксис C++, да, но не законный.

Правая часть оператора присваивания должна быть полностью оценена во время назначения. В этом случае это i, который не инициализирован.

Кредитов Steve Джессоп, который выкопал цитату:

4,1/1, именующие-к-Rvalue преобразование

[...], если объект не инициализирован, программа что требует преобразования имеет неопределенное поведение.

+0

Этот аргумент является круглым, имеющий переменную, значение которой не определено, конечно, не является незаконным. Сравните его с простым «int i;», который также оставляет «i» с неопределенным значением. – unwind

+0

@nhahtdh, что не значит, что это законно. –

+0

Luchian, он компилируется, и в спецификации нет ничего, что бы его не разрешало. – OmnipotentEntity

7

Вы можете позволить g++ предупредить вас об этом прецеденте с -Winit-self (в сочетании с -Wuninitialized), и если рассматривать предупреждения как ошибки, он должен удовлетворять ваш зуд.

Этот метод использования конструктора копирования для самоинициализации иногда используется для подавления конструктора/инициализатора по умолчанию для глобального объекта по умолчанию. Это может потребоваться, если конструктор по умолчанию глобального объекта находится только в 0, инициализируя объект, но объект был использован до того, как конструктор будет выполнен. В качестве возврата к C глобальные переменные 0 инициализируются при запуске программы, а затем среда выполнения C++ начинает выполнять глобальные конструкторы. В тех узких случаях, когда определенный конструктор, который выполнил бы, должен был только 0 из объекта, само инициализация не наносит никакого вреда.

В общем случае самоинициализация конструктора копирования является плохой практикой, поскольку она обычно вызывает те же проблемы, что и при использовании неинициализированной переменной (то есть неопределенное поведение). В конкретном примере в вопросе ОП i является локальным для main и поэтому неинициализирован. Результатом чтения неинициализированной переменной всегда является неопределенное поведение.

+0

Он, похоже, не генерирует никаких предупреждений с '-Winit-self' для меня. – nhahtdh

+0

@nhahtdh: Спасибо, я обновил сообщение. С уважением – jxh

+0

_If_ у объекта есть определенный пользователем тип, а конструктор принимает экземпляр по ссылке и никогда не разыгрывает его (в конструкторе), тогда код является законным. (Определенный пользователем конструктор может сохранить адрес, например.) –

14

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

struct ThingManager { 
    void *thing; 
    ThingManager(void *thing) : thing(thing) {} 
    void Speak() { 
     if (thing == (void*)this) { 
      std::cout << "I'm managing myself\n"; 
     } else { 
      std::cout << "I'm managing " << thing << "\n"; 
     } 
    } 
}; 

ThingManager self_manager(&self_manager); 
ThingManager other_manager(&self_manager); 

Так C++ позволяет ссылаться на объект в своем собственном выражении инициализатора (его имя находится в области).Затем, как и в C++, ваша проблема заключается в том, чтобы убедиться, что вы фактически не используете неинициализированное значение (в вашем примере int i = i; использует неинициализированное значение).

Компилятор может помочь с определением использования неинициализированных значений, но стандарт не требует этого.

2

Вы можете использовать любую ранее объявленную переменную в качестве инициализатора другой переменной.

В этом случае, как только компилятор проанализирует int i, он добавляет это в таблицу символов, поэтому, когда он видит инициализатор = i, символ может быть разрешен из предыдущего объявления.

Это не ошибка, потому что компилятор может понять, что он может генерировать код, который однозначно выполняет именно то, что указывает исходный код, семь, если оно семантически подозревается. Философия C и C++ заключается в компиляции всего, что может быть скомпилировано синтаксически. Семантические ошибки обычно вызывают только предупреждения, и только тогда, если такие предупреждения включены.

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