TransWikia.com

Конструктор по-умолчанию для константного класса

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’

2 Answers

В первом случае 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 type T is const-default-constructible if default-initialization of T would invoke a user-provided constructor of T (not inherited from a base class) or if

(7.4) — each direct ... non-static data member M of T has a default member initializer or, if M is of class type X (or array thereof), X is const-default-constructible,
...
If a program calls for the default-initialization of an object of a const-qualified type T, 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

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP