Почему возникает ошибка default member initializer needed within definition of enclosing class outside of member functions?
Почему вот такой код компилируется
#include <vector>
#include <map>
struct A{
int x;
std::vector<int> y{};
};
struct B {
static inline const std::map<int, A> m {
{42, A{42}},
};
};
а вот такой уже нет
#include <vector>
#include <map>
struct B {
struct A{
int x;
std::vector<int> y{};
};
static inline const std::map<int, A> m {
{42, A{42}},
};
};
Ошибка
error: default member initializer for 'y' needed within definition of enclosing class 'B' outside of member functions
Ответы (1 шт):
Исправлять так:
static inline const std::map<int, A> m {
{42, A{42, {}}},
};
Либо можно дописать конструктор в A. (Причем A() = default; не помогает, если бы вам был нужен конструктор по умолчанию, а A() {} помогает.)
Компиляторы обрабатывают классы в два прохода. Во второй проход обрабатываются тела функций (поэтому они могут использовать члены, объявленные ниже них самих). По-видимому, инициализаторы нестатических переменных тоже обрабатываются во второй проход.
Каждый не-вложенный отдельный класс обрабатывается отдельно. Т.е. в вашем первом примере - сначала оба прохода по A, потом оба прохода по B.
А вложенные классы обрабатываются вместе с окружающим их классом. Т.е. во втором примере во время первого прохода по A выполняется и первый проход по B, а потом аналогично во время второго прохода по A выполняется и второй проход по B.
Инициализатор m обрабатывается в первый проход, потому что она статическая. И если не указать все поля в A{...}, тут нужен инициализатор по умолчанию члена из A, который еще не обработался, потому что для нестатических переменных это происходит во второй проход.
Но если честно, это все выглядит странно. Почему не обрабатывать инициализаторы статических переменных тоже во второй проход? Для static constexpr это не подходит, потому что static constexpr int a = 10; int b[a];, но могли бы только для не-constexpr сделать.
В вашем примере это исправляется легко, а бывает еще вот так:
#include <type_traits>
#include <variant>
#include <vector>
struct B {
struct A{
int x;
std::vector<int> y{};
};
std::variant<A> v;
};
B b; // error: call to implicitly-deleted default constructor of 'B'
static_assert(std::is_default_constructible_v<B::A>); // error: static assertion failed
На это ругается только Clang, и кажется, что в этом варианте это уже баг кланга и/или стандарта: https://github.com/llvm/llvm-project/issues/60321
Тут не обнаруживается весь конструктор по умолчанию A(), а не конструктор конкретного его поля. Причем происходит это тихо: в конструкторе по умолчанию std::variant проверяется std::is_default_constructible_v для его первого шаблонного аргумента, и тихо возвращает false, видимо потому что инициализаторы полей по умолчанию еще не обработались (но это тупо, могли бы просто предположить, что раз они есть, то все ок).
Дальше std::is_default_constructible_v<B::A> == false кешируется, что видно про срабатывающему static_assert ниже. И отключенность конструктора std::variant тоже кешируется, из-за чего B b; не работает.
И вот это уже непонятно как чинить. std::variant<A> v{A{0, {}}}; делает так, что B b; конструируется, но ассерт все равно не проходит.