Инициализация по умолчанию VS2013 против инициализации значения

Рассмотрим код ниже

struct B
{
B() : member{}{};
int member[10];
};

int main()
{
B b;
}

Компилятор VS2013 выдает следующее предупреждение:

предупреждение C4351: новое поведение: элементы массива ‘B :: member’ будут
инициализация по умолчанию 1> test.vcxproj ->
C: \ Users \ asaxena2 \ Documents \ visual studio
2013 \ Projects \ Test \ Debug \ test.exe

Это задокументировано Вот

С C ++ 11 и применением концепции «инициализации по умолчанию» означает, что элементы B.member не будут инициализированы.

Но я верю, что member{} должна выполнить инициализацию значения, а не инициализацию по умолчанию. Не сломан ли компилятор VS2013?

$ 8,5 / 6

По умолчанию инициализировать объект типа T средства:
— если T является (возможно, cv-квалифицированным) типом класса (раздел 9), конструктором по умолчанию для T называется (и инициализация плохо сформирована, если T не имеет доступного конструктора по умолчанию);
— если T тип массива, каждый элемент инициализируется по умолчанию;
— иначе инициализация не выполняется.
Если программа требует инициализации по умолчанию объекта constквалифицированный тип T, T должен быть типом класса с предоставленным пользователем конструктором по умолчанию.

$ 8.5.1

Инициализация списка объекта или ссылки типа T определяется следующим образом:
— Если в списке инициализатора нет элементов и T тип класса с конструктором по умолчанию, объект инициализируется значением
— В противном случае, если T является агрегатом, выполняется агрегатная инициализация (8.5.1).

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

  struct S { int a; const char* b; int c; };
S ss = { 1, "asdf" };

инициализирует ss.a с 1, ss.b с "asdf", а также ss.c со значением выражения формы int(), то есть, 0, — конец примера ]

9

Решение

Похоже, это неправильно сформулированное предупреждающее сообщение (и я удивлен, что оно печатает предупреждение в первую очередь), но поведение правильное. B::member инициализируется значение, которое для массива int превращается в нулевую инициализацию. Это можно продемонстрировать с помощью следующего:

#include <iostream>

struct B
{
B() : member{}{};
int member[10];
};

struct C
{
C() {};
int member[10];
};

int main()
{
B b;
for(auto const& a : b.member) std::cout << a << ' ';
std::cout << std::endl;

C c;
for(auto const& a : c.member) std::cout << a << ' ';
std::cout << std::endl;
}

Если вы компилируете и запускаете в отлаживать режим это приводит к выводу:

0 0 0 0 0 0 0 0 0 0
-858993460 -858993460 -858993460 -858993460 -858993460 -858993460 -858993460 -858993460 -858993460 -858993460

Числа во второй строке 0xCCCCCCCCшаблон отладки, которым компилятор VC ++ заполняет память в режиме отладки. таким образом B::member инициализируется нулями, в то время как инициализация не выполняется для C::member,

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

7

Другие решения

Предупреждение компилятора неверно; он фактически выполняет инициализацию значения, как того требует стандарт.

Пример:

#include <iostream>

struct B {
B() : member{}{};
int member[10];
};

int main() {
int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
B &b = *new (a) B;
std::cout << b.member[9];  // prints '0'
}
2

Страница MSDN говорит:

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

Итак, вы должны добавить #pragma warning (suppress:4351) за одну строку или #pragma warning (disable:4351) для всего файла.

0
По вопросам рекламы [email protected]