Почему это компилируется (используется в функции до инициализации)?


Рассмотрим этот код (используя CString, потому что он знаком и легко виден, когда не построен, но ничего особенного в классе), протестированный в Visual Studio 2008:

CString DoSomething( const CString& sString )
{
    return sString;
}

CString sTest1 = DoSomething( sTest1 ); // Compiles (no warnings), fails at runtime
CString sTest2( DoSomething( sTest2 ) ); // Doesn't compile
CString sTest3; sTest3 = DoSomething( sTest3 ); // Compiles, self-assignment, works

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

Теперь я понимаю, почему Test1 не работает и почему Test2 не компилируется. Меня интересует, почему Test1 компилируется в первую очередь? Это разрешено в стандарте, открыто для интерпретации, дефект в компиляторе VS2008, недостаток в статической проверке init-before-use или что? Есть ли какой-либо способ заставить компилятор по крайней мере выдать предупреждение в этом случае (Test1, по-видимому, компилируется чисто с максимальным уровнем предупреждения под VS2008)? Каким было бы оправдание для этого? спецификация C++, разрешающая эту конструкцию?

Edit: существует ли альтернативный способ заставить компилятор компилировать Test1 как Test2 (и таким образом вызвать ошибку)?

Изменить, чтобы добавить дословное сообщение об ошибке для Test2: ошибка C2065: 'sTest2': необъявленный идентификатор

2   4  

2 ответа:

Поведение, которое вы видите в sTest1, не определено в стандарте C++. Это странно и неправильно, но он компилируется на нескольких компиляторах.

Смотрите ответ litb на следующий поток для получения более подробной информации: Метод, запущенный на объекте до инициализации объекта?

За последние несколько дней было задано несколько вопросов о связанных с этим явлениях.

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

Я предполагаю, что возврат является проблемой во время выполнения, потому что вы пытаетесь сделать возврат по значению (конструктор копирования) того, что по сути является неинициализированным пространством памяти, которое должно было быть строкой. В зависимости от того, как хранятся CStrings, код, вероятно, ищет нулевой Терминатор или что-то, что представляет количество выделенных байтов.