2014-11-02 5 views
16

У меня есть шаблонный номер gray_code, который предназначен для хранения некоторого целого числа без знака, базовые биты которого хранятся в коде кода Серы. Вот оно:Инициализация значения: инициализация по умолчанию или инициализация нуля?

template<typename UnsignedInt> 
struct gray_code 
{ 
    static_assert(std::is_unsigned<UnsignedInt>::value, 
        "gray code only supports built-in unsigned integers"); 

    // Variable containing the gray code 
    UnsignedInt value; 

    // Default constructor 
    constexpr gray_code() 
     = default; 

    // Construction from UnsignedInt 
    constexpr explicit gray_code(UnsignedInt value): 
     value((value >> 1)^value) 
    {} 

    // Other methods... 
}; 

В некоторых родового алгоритма, я написал что-то вроде этого:

template<typename UnsignedInt> 
void foo(/* ... */) 
{ 
    gray_code<UnsignedInt> bar{}; 
    // Other stuff... 
} 

В этой части кода, я ожидал bar быть нулевым intialized и поэтому bar.value быть нуль- инициализируется. Однако после того, как вы столкнулись с неожиданными ошибками, оказалось, что bar.value инициализирован мусором (точнее, 4606858) вместо 0u. Это удивило меня, так что я пошел в cppreference.com, чтобы увидеть, что линия выше, именно должен делать ...


Из того, что я могу читать, форма T object{}; соответствует value initialization. Я нашел эту цитату интересной:

Во всех случаях, если используется пустая пара фигурных скобок {}, а T является совокупным типом, вместо инициализации значения выполняется инициализация агрегата.

Однако gray_code имеет предоставленный пользователем конструктор. Поэтому он не является агрегатом, поэтому aggregate initialization не выполняется. gray_code не имеет конструктора, принимающего std::initializer_list, поэтому list initialization не выполняется. Значение инициализируется из gray_code должен затем следовать обычные C++ 14 правил инициализации значений:

1) Если T является типом класса с конструктором не по умолчанию или с помощью предоставленного пользователем конструктора по умолчанию или с удалено default конструктор, объект инициализируется по умолчанию.

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

3) Если T - тип массива, каждый элемент массива инициализируется значением.

4) В противном случае объект инициализируется нулем.

Если я правильно прочитал, gray_code имеет явно установленный по умолчанию (не предоставленный пользователем) конструктор по умолчанию, поэтому 1) не применяется. Он имеет стандартный конструктор по умолчанию, поэтому 2) применяется: gray_code - zero-initialized. По умолчанию конструктор по умолчанию, похоже, отвечает всем требованиям тривиального конструктора по умолчанию, поэтому инициализация по умолчанию не должна выполняться. Давайте посмотрим тогда, как gray_code равна нулю инициализирован:

  • Если T является скалярный тип, начальное значение объекта является неотъемлемой постоянной нулевой неявно преобразуется в T.

  • Если T является классом типа неединичного класса, все базовые классы и нестатические элементы данных инициализируются нулями, а все заполнение инициализируются нулевыми битами. Конструкторы, если они есть, игнорируются.

  • Если T является типом объединения, первый нестатический именованный элемент данных инициализируется нулем, и все отступы инициализируются нулевыми битами.

  • Если Т тип массива, каждый элемент равен нулю инициализирован

  • Если Т ссылочный тип, ничего не делается.

gray_code тип несрастание класса. Поэтому все его нестатические элементы данных должны быть инициализированы, что означает, что value инициализируется нулем. value удовлетворяет std::is_unsigned и поэтому является скалярным типом, что означает, что он должен быть инициализирован «интегральной константой нуля, неявно преобразованной в T».

Итак, если я правильно прочитал все, что в функции foo выше, bar.value должен всегда быть инициализированы 0 и он никогда не должен быть инициализирован с мусором, я прав?

Примечание: компилятор Я скомпилировал свой код с помощью MinGW_w4 GCC 4.9.1 с (потоки POSIX и карликовые исключения) в случае, если это помогает. Хотя я иногда получаю мусор на своем компьютере, мне никогда не удавалось получить ничего, кроме нуля, с онлайн-компиляторами.


Update: кажется быть GCC ошибка, что ошибка моя и не то, что мой компилятор. На самом деле, при написании этого вопроса, я предположил, для простоты, что

class foo { 
    foo() = default; 
}; 

и

class foo { 
    foo(); 
}; 

foo::foo() = default; 

были эквивалентны. Они не. Вот цитата из стандартного раздела C++ 14 [dcl.fct.def.default]:

функция предоставленного пользователем если пользователь объявленного и явно не дефолт или удален по его первой декларации.

Другими словами, когда я получил значения мусора, мой дефолтный конструктор по умолчанию был действительно предоставлен пользователем, поскольку он не был явно искажен в его первом объявлении. Поэтому произошло не инициализация нуля, а инициализация по умолчанию. Спасибо @Columbo снова за то, что он указал на настоящую проблему.

+4

[dcl.fct.def.default]/5 "функция предоставляется пользователем, если она объявлена ​​пользователем и ** явно не установлена ​​по умолчанию ** или удалена в ее первом объявлении". Ваш конструктор не предоставляется пользователям. – user657267

+0

Вы могли бы написать 'UnsignedInt value {0}' вместо чтения страниц и страниц ... Я знаю, что вы знаете. ;-) – davidhigh

+1

примечание ... инициализация значения STILL не работает на компиляторе Microsoft по сравнению с VS2013 – Mgetz

ответ

9

Итак, если я правильно прочитал все, что в функции foo выше, bar.value всегда должен быть инициализирован с 0 и он никогда не должен быть инициализируется с мусором, я прав?

Да. Ваш объект инициализируется прямым списком. C++ 14-х * [dcl.init.list]/3 указывает, что

Список инициализация объекта или ссылки типа T определяется следующим образом:

  • [... НЕПРИМЕНИМЫЕ пуля пункты ...]

  • В противном случае, если T является агрегатной, выполняется агрегатная инициализация (8.5.1).

  • В противном случае, если в списке инициализаторов нет элементов, а T - тип класса с конструктором по умолчанию, объект инициализируется значением.

  • [...]

Ваш класс не совокупность, поскольку она имеет предоставленный пользователь конструкторов, но у него есть конструктор по умолчанию. [Dcl.init]/7:

Чтобы значение инициализации объект типа T означает:

  • , если T является (возможно, резюме квалифицированных) тип класса (пункт 9) с конструктором по умолчанию (12.1) или конструктором по умолчанию, который предоставляется или удаляется пользователем, тогда объект инициализируется по умолчанию;

  • если Т (возможно, резюме квалифицированных) типа класса без предоставленного пользователем или удален конструктор по умолчанию, то объект является нулевой инициализируется и семантические ограничения для по умолчанию инициализации проверяются, и если T имеет нетривиальный конструктор по умолчанию , объект инициализируется по умолчанию;

[dcl.fct.def.default]/4:

Специальная функция является членом предоставленный пользователем если пользователь объявленной и явно не по умолчанию [ ...] по его первой декларации.

Таким образом, ваш конструктор не является пользователем, поэтому объект инициализируется нулем.(Конструктор не вызывается, так как его тривиальным)

И, наконец, в случае, если это не было ясно, чтобы нулевой инициализировать объект или ссылка типа T означает:

  • , если T является скалярным типом (3.9), объект инициализируется значением, полученным преобразованием целочисленного литерала 0 (ноль) в T;

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

  • [...]


Таким образом, либо

  • Ваш компилятор прослушивается

  • ... или ваш код вызывает неопределенное поведение в какой-то другой точке.


* Ответ по-прежнему есть в C++ 11, хотя приведенные разделы не эквивалентны.

+0

Я посмотрел на сгенерированную сборку. Конструктор по умолчанию имеет одну значимую инструкцию: «mov% ecx, -0x4 (% ebp)». Если я хорошо понимаю, он устанавливает адрес 'bar', но никогда не устанавливает' value'. – Morwenn

+0

@Morwenn Конечно, конструктор не устанавливает 'значение'. Неявно созданный по умолчанию конструктор по умолчанию инициализирует объекты-члены, что означает, что для скаляров не выполняется инициализация. – Columbo

+0

Вы правы, мне кажется, что я запутался между конструктором и частью инициализации. Тем не менее, в моем случае вызывается дефолтный конструктор по умолчанию. Но ничто другое возле этого сайта вызова, похоже, не инициализирует 'значение' в любом случае. – Morwenn

Смежные вопросы