Явный конструктор по умолчанию и агрегаты по умолчанию
Как объяснить разницу, когда я компилирую #if 0
а также #if 1
версии следующего кода:
#include <cstdlib>
struct A
{
explicit A() = default; // explicitly defaulted or deleted constructors are allowed for aggregates (since C++11)
#if 1
private :
#endif
int i;
};
int
main()
{
A a = {};
return EXIT_SUCCESS;
}
- за
#if 0
все хорошо, компиляция прошла успешно. - за
#if 1
ошибка компиляции с сообщением об ошибке:ошибка: выбранный конструктор является явным в инициализации копирования
Какая разница для выражения A a = {};
в зависимости от того A
это агрегат или нет?
1 ответ
TL;DR: Clang и GCC не правы в отклонении вашего кода. Разрешение CWG 1630 сделало правильную инициализацию по умолчанию независимо от того, выбранный конструктор по умолчанию explicit
или нет.
В варианте вашего кода, в котором i
является private
, A
не является агрегатом, так как они не могут иметь частных членов. Пока i
является public
, тем не мение, A
является агрегатом1, и конструктор не вызывается, поскольку выполняется инициализация агрегата (см. синее поле), поэтому ваш конструктор explicit
не имеет значения.
Однако, как только вы представите закрытый член, вам потребуется инициализация значения согласно красному полю. Следовательно, [dcl.init]/(8.2) применяется:
[dcl.init] / (7.1) определяет инициализацию по умолчанию для этого случая:
И §13.3.1.3 дает
Для […] default-initialization все функции-кандидаты являются конструкторами класса инициализируемого объекта.
Ни в коем случае не рассматривается исходный контекст - копирование или прямая инициализация. (§13.3.1.7 также не применяется.) Фактически, это предназначено; см. CWG # 1518:
Эта проблема решена путем разрешения проблемы 1630: при инициализации по умолчанию теперь используется 13.3.1.3 [over.match.ctor], которая теперь разрешает явные конструкторы для инициализации по умолчанию.
Clang и GCC (и VC++) еще не реализовали соответствующий DR и, следовательно, неверны при отклонении кода в режиме C++14.
1) У вашего класса есть конструктор, объявленный пользователем, но он непредоставляется пользователем, то есть не препятствует вашему классу быть агрегатом. Напомним определение в [dcl.init.aggr]/1:
Агрегат - это массив или класс (раздел 9) без предоставленных пользователем конструкторов (12.1), без закрытых или защищенных нестатических элементов данных (пункт 11), без базовых классов (пункт 10) и без виртуальных функций (10.3).