Предупреждение C4345 Visual Studio неправильно? - PullRequest
20 голосов
/ 02 января 2012

Следующий код вызывает 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 с, а вторая версия напечатает пять 5 с.Таким образом, первая версия действительно инициализируется значением (а базовый буфер инициализируется нулями), хотя MSVC заявляет, что не будет.

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

Ответы [ 3 ]

13 голосов
/ 02 января 2012

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


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

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

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

std::array это тип класса. Для типов классов (8,5 [dcl.init]):

К default-initialize объект типа T означает:

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

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

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


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

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

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

А это означает инициализацию значения для всех элементов.

Нет, вот правило (8,5 [dcl.init]):

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

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

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

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

1 голос
/ 02 января 2012

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

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

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

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

0 голосов
/ 02 января 2012

Это предупреждение относится исключительно к изменению, которое произошло в том, как Visual Studio интерпретирует и компилирует этот оператор из предыдущей версии. «Изменение поведения» означает, что «если вы написали это на более старом VC ++, будьте осторожны, все изменилось». Нельзя сказать, что первый пример эквивалентен второму.

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

Как правило, объект POD, который является новым для размещения, НЕ ДОЛЖЕН инициализироваться по умолчанию, поэтому это предупреждение правильно, сообщая, что () сделает это возможным. Тем не менее, это не является предупреждением против этого как такового, но, как я уже сказал, 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 часто неправильно понимают.

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