Рассмотрим код ниже
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
, — конец примера ]
Похоже, это неправильно сформулированное предупреждающее сообщение (и я удивлен, что оно печатает предупреждение в первую очередь), но поведение правильное. 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
,
Отказ от ответственности: Я знаю, что чтение из неинициализированной переменной является неопределенным поведением, но это лучшее доказательство Я мог бы придумать.
Предупреждение компилятора неверно; он фактически выполняет инициализацию значения, как того требует стандарт.
Пример:
#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'
}
Страница MSDN говорит:
C4351 означает, что вы должны проверить свой код … Если вы хотите новый
поведение, которое, вероятно, потому что массив был явно добавлен в
список инициализации члена конструктора, используйте предупреждение прагма
отключить предупреждение. Новое поведение должно быть хорошо для большинства
пользователи.
Итак, вы должны добавить #pragma warning (suppress:4351)
за одну строку или #pragma warning (disable:4351)
для всего файла.