Каковы правила создания поэтапного конструктора?

Я обнаружил, что возможность использования синтаксиса списка инициализаторов для класса зависит от того, имеют ли поля класса значения по умолчанию. Почему?

Чтобы быть точным, рассмотрим следующий код:

class S
{
    public:
        int a;
};
...
int a;
S s{ a };

Он компилируется без каких-либо проблем. Но если я добавлю значение по умолчанию в поле класса, он перестанет строить:

class S
{
    public:
        int a = 0;
};
...
int a;
S s{ a };

Ошибка 1 ошибка C2440: 'initializing': не может преобразовать из 'initializer-list' в 'S'

Почему? Что еще влияет на создание такого конструктора?

Ответы

Ответ 1

В С++ 14 ваш код действителен и должен компилироваться с любым совместимым с С++ 14 компилятором.


В С++ 11:

Если у вас нет значения по умолчанию для a, ваш тип является агрегатом, и таким образом агрегатная инициализация может быть выполнена:

Агрегат является одним из следующих типов:

  • тип массива

  • тип класса (как правило, struct или union), который имеет

    • нет частных или защищенных нестатических элементов данных
    • нет конструкторов, предоставляемых пользователем, включая те, которые унаследованы от общих баз (начиная с С++ 17) (разрешены явно дефолтные или удаленные конструкторы) (начиная с С++ 11)
    • нет виртуальных, частных или защищенных (начиная с С++ 17) базовых классов
    • нет виртуальных функций-членов
    • нет инициализаторов элемента по умолчанию (, поскольку С++ 11, до С++ 14)

Как только вы добавите значение по умолчанию для атрибута a, ваша агрегатная инициализация больше не будет выполняться, так как ваш тип перестает быть агрегатом.

Ответ 2

Показанный код компилируется без каких-либо проблем с gcc 6.1.1. Вероятно, вы используете старый компилятор, который не поддерживает полностью С++ 14:

$ cat t.C
class S
{
public:
  int a = 0;
};


void foo()
{
    int a=4;

    S s{a};
}
$ g++ -std=c++1z -g -c -o t.o t.C
$ g++ --version
g++ (GCC) 6.1.1 20160510 (Red Hat 6.1.1-2)
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Ответ 3

В обоих случаях конструктор по умолчанию S не принимает аргументов. Форма класса не влияет на генерацию конструктора по умолчанию. Кроме того, нет неявно сгенерированного конструктора, принимающего int.

Если S является агрегатом, то использование S s = { arguments_opt }; не вызывает конструктор S. Вместо этого он вызывает что-то, называемое агрегатной инициализацией. Агрегаты - это единственные классы, которые могут быть созданы объектами этого класса без вызова конструктора.

Только если S является не агрегатом, то S s = { arguments_opt }; пытается сопоставить список аргументов с параметрами конструктора S.

(Как объяснялось другими, в С++ 11, предоставление элемента для выравнивания или выравнивания для нестатического элемента данных делает класс не агрегатом).