Stack Overflow на русском Asked by Oleg on August 30, 2021
Почему эта программа не компилируется?
#include <iostream>
struct A {
A() = default;
int x;
};
int main() {
const A a;
std::cout << a.x << 'n';
}
А вот эта компилируется?
#include <iostream>
struct A {
A();
int x;
};
A::A() = default;
int main() {
const A a;
std::cout << a.x << 'n';
}
И эта тоже работает:
#include <iostream>
struct A {
A() = default;
int x;
};
int main() {
const A a{};
std::cout << a.x << 'n';
}
Ошибка компиляции:
error: uninitialized ‘const a’
В первом случае A() = default;
не производит инициализацию x
. Класс остается тривиально инициализируемым. static_assert(std::is_trivially_constructible_v<A>);
Во втором сначала объявляется пользовательский конструктор по-умолчанию A();
, который затем генерируется и инициализирует x
. Класс уже не будет тривильно инициализируемым static_assert(not std::is_trivially_constructible_v<A>);
В третьем неинициализированные поля инициализируются на месте const A a{};
за счет использования синтаксиса direct list initialization.
Correct answer by user7860670 on August 30, 2021
Тривиальность конструктора тут не при чем. Если добавить к классу поле с нетривиальным конструктором (напримерstd::string
), то std::is_trivially_constructible
будет в любом случае возвращать false
, а поведение не изменится.
У конструктора по умолчанию, автоматически сгенерированного компилятором (не наш случай), или объявленного как =default
внутри тела класса (наш случай) есть особое свойство, которое нельзя повторить в самодельном конструкторе.
Поведение такого конструктора зависит от того, были ли использованы скобки ()
или {}
при его вызове (value initialization), или нет (default initialization):
Если скобок нет, то он ведет себя как обычно.
Если скобки есть, то поля, которые иначе остались бы неинициализированными, зануляются (value initialization).
А если конструктор объявлен как =default
снаружи тела класса, то у него такого специального поведения не будет. Насколько я знаю, он ничем не будет отличаться от пустого конструктора ({}
вместо =default
).
Так вот. В первом случае сработала защита от дурака, про которую я раньше не слышал. (В духе запрета на объявления вроде const int x;
без инициализатора.)
[dcl.init]/7
...
A class typeT
is const-default-constructible if default-initialization ofT
would invoke a user-provided constructor ofT
(not inherited from a base class) or if(7.4) — each direct ... non-static data member
M
ofT
has a default member initializer or, ifM
is of class typeX
(or array thereof),X
is const-default-constructible,
...
If a program calls for the default-initialization of an object of a const-qualified typeT
,T
shall be a const-default-constructible class type or array thereof.
То есть ошибку вызывает default-инициализация (то есть без использования скобок ()
или {}
, с ними была бы value-инициализация) константного экземпляра класса, у которого конструктор по умолчанию не user-provided (то есть сгенерирован компилятором автоматически, или отмечен как =default
внутри тела класса), если у класса есть поля, котороые при таком раскладе остались бы неинициализированными.
В третьем случае x
инициализируется нулем, и параграф выше не применяется (и то и другое - потому что для инициализации используются скобки).
Во втором случае x
остается неинициализированным, но никакой защиты от этого нет.
Можно догадаться почему:
Во-первых, в общем случае сложно определить, присваивает ли конструктор какое-то значение полю или нет.
Во-вторых, определение конструктора могло быть спрятано в отдельную единицу трансляции, и хотя в здесь этого сделано не было, компилятор все равно не смотрит в тело конструктора. Предположительно для того, чтобы перемещение его в отдельную единицу трансляции не меняло поведение кода.
Answered by HolyBlackCat on August 30, 2021
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP