У меня есть шаблонный номер 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 снова за то, что он указал на настоящую проблему.
[dcl.fct.def.default]/5 "функция предоставляется пользователем, если она объявлена пользователем и ** явно не установлена по умолчанию ** или удалена в ее первом объявлении". Ваш конструктор не предоставляется пользователям. – user657267
Вы могли бы написать 'UnsignedInt value {0}' вместо чтения страниц и страниц ... Я знаю, что вы знаете. ;-) – davidhigh
примечание ... инициализация значения STILL не работает на компиляторе Microsoft по сравнению с VS2013 – Mgetz