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

Является ли предупреждение C4345 о Visual Studio неправильным?

Следующий код запускает C4345 на отмеченной строке:

#include <array>
#include <iostream>

int main(){
    static unsigned const buf_size = 5;
    typedef std::array<char, buf_size> buf_type;

    char buf[] = { 5, 5, 5, 5, 5 };
    void* p = &buf[0];
    buf_type* pbuf = new (p) buf_type(); // <=== #10

    for(unsigned i=0; i < buf_size; ++i)
        std::cout << (char)((*pbuf)[i] + 0x30) << ' ';
}

main.cpp(10): предупреждение C4345: изменение поведения: объект типа POD, построенный с инициализатором формы(), будет инициализирован по умолчанию

Итак, согласно их предупреждению, строка 10 должна иметь такое же поведение, как если бы оно было написано как

buf_type* pbuf = new (p) buf_type; // note the missing '()'

Однако выход отличается. А именно, первая версия будет печатать пять 0 s, а вторая версия будет печатать пять 5 s. Таким образом, первая версия действительно инициализируется инициализацией (и базовый буфер с нулевой инициализацией), хотя MSVC говорит, что это не будет.

Можно ли это считать ошибкой в ​​MSVC? Или я неверно истолковал предупреждение/неисправен мой тестовый код?

4b9b3361

Ответ 1

TL; версия DR: поведение MSVC на самом деле корректно, хотя предупреждение неверно (оно должно означать инициализацию значения).


Для new (p) buf_type;, MSVC корректно выполняет инициализацию по умолчанию, потому что стандарт (5.3.4 [expr.new]) требует:

Новое выражение, создающее объект типа T, инициализирует этот объект следующим образом:

  • Если новый инициализатор опущен, объект инициализируется по умолчанию (8.5); если инициализация не выполняется, объект имеет неопределенное значение.
  • В противном случае новый-инициализатор интерпретируется в соответствии с правилами инициализации 8.5 для прямой инициализации.

std::array - тип класса. Для типов классов (8.5 [dcl.init]):

По умолчанию инициализировать объект типа T означает:

  • Если T является (возможно, cv-квалифицированным) типом класса, вызывается конструктор по умолчанию для T (и инициализация плохо сформирована, если T не имеет доступного конструктора по умолчанию);

default-initialization оставляет память неизменной только для примитивных типов (и необработанных массивов)

С другой стороны, std::array имеет стандартный конструктор по умолчанию, поэтому сами члены должны инициализироваться по умолчанию. И на самом деле вы это заметили.


Затем в соответствии с тем же разделом версия new (p) buf_type(); вызывает прямую инициализацию.

std::array - это совокупность, поэтому я думаю, что это правило (8.5.1 [dcl.init.aggr]) применяется:

Если в списке меньше предложений-инициализаторов, чем в агрегате, то каждый член, явно не инициализированный, должен быть инициализирован из пустого списка инициализаторов (8.5.4).

И это означает инициализацию значения для всех элементов. Забастовкa >

Нет, здесь правило (8.5 [dcl.init]):

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

Инициализация значения агрегата означает инициализацию значения всех элементов, поскольку элементы примитивны, что означает нулевое заполнение.

Таким образом, поведение MSVC действительно корректно, хотя предупреждение неверно (оно должно означать инициализацию значения).

Об этом уже сообщалось, см.

Ответ 2

Вот как я прочитал статью MS http://msdn.microsoft.com/en-us/library/wewb47ee%28v=vs.80%29.aspx

В старой версии VS (ваш оригинальный синтаксис) - в этой статье VS 2003 - инициализация POD инициализируется по умолчанию с помощью 0s. Ваш код будет поддерживать это.

В новой версии VS - в этой статье это будет VS 2005 - программист должен инициализировать POD явно, поскольку инициализация по умолчанию не выполнялась. В вашем случае, как показано в вашем коде, он правильно отображает 5 секунд.

Таким образом, когда я прочитал его, старый синтаксис инициализировал POD до 0, даже если ваш код уже явно инициализировал его. Я добавляю отказ от ответственности, что здесь очень поздно, и я устал, поэтому все это может лепет.

Ответ 3

Это предупреждение относится строго к изменению, которое произошло в том, как Visual Studio интерпретирует и компилирует этот оператор из предыдущей версии. "Изменение поведения" означает, что "если вы написали это со старым VС++, следите, все изменилось". Это не означает, что первый пример эквивалентен второму.

Фактически, это компилятор, соответствующий ближе к стандарту С++. Это не предупреждение, что эти вещи будут эквивалентны - это предупреждение о том, что они ОДИН ИЗ БЫЛИ, но теперь нет. Вы экстраполировали то, что "инициализировано по умолчанию" означает неверно.

Обычно объект POD, который является местом размещения-new'd, НЕ ДОЛЖЕН быть инициализирован по умолчанию, поэтому это предупреждение верно, говоря вам, что() сделает это. Тем не менее, это не предупреждение против этого, как таковое, но, как я уже сказал, Microsoft заметила изменения.

Гарантируется std:: array (так говорит документация MSDN) как тип POD, когда поставляемый тип является POD (например, char). Тип POD - это в основном простые старые данные: компилятор рассматривает его так, как если бы это был просто объект. Несмотря на то, что это класс, на нем не выполняется инициализация, если явно не требуется, так же, как он не будет инициализировать стандартный указатель массива C (и, по сути, не может).

Перейдите к некоторому C-коду для некоторого освещения.

// POD version.
// buf_type = new (p) buf_type;
typedef char buf_type;
buf_type *pbuf = p;             // Pointer is assigned

и

// Constructed version.
// buf_type = new (p) buf_type();
void construct_buf_type(buf_type *what);

typedef char buf_type;
buf_type *pbuf = p;             // Pointer is assigned
construct_buf_type(buf_type);   // Constructor is called which default-initializes it

То, что происходит за кулисами, немного отличается, но это концептуально то, что происходит. Оба оператора сообщают компилятору о размещении объекта в этом месте; тот, у которого есть() в конце, сообщает, что впоследствии он вызывает конструктор по умолчанию.

Да, это может быть раздражающей синтаксической двусмысленностью, если вы забудете свой() в своем новом, когда пишете объекты POD. Но почти никто никогда не делает, что является частью того, почему POD часто неправильно поняты.