Подтвердить что ты не робот

Явный конструктор по умолчанию и агрегаты по умолчанию

Как объяснить разницу, когда я компилирую версии #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 аггрегатом или нет?

4b9b3361

Ответ 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 дается

Для [...] инициализации по умолчанию кандидат функции - все конструкторы класса объекта, являющегося инициализированы.

Ни в коем случае не рассматривается исходный контекст - копирование или прямая инициализация. (П. 13.3.1.7 также не применяется). На самом деле это предназначено; см. CWG # 1518:

Эта проблема разрешена решением issue 1630: инициализация по умолчанию теперь использует 13.3.1.3 [over.match.ctor ], который теперь позволяет явным конструкторам инициализации по умолчанию.

Clang и GCC (и VС++) еще не реализовали соответствующий DR и, таким образом, неверны при отклонении кода в режиме С++ 14.


1)  У вашего класса есть конструктор, объявленный пользователем, но он не предоставляется пользователям, то есть не мешает вашему классу быть агрегированным. Напомним определение в [dcl.init.aggr]/1:

Агрегат - это массив или класс (раздел 9) без каких-либо пользовательских конструкторы (12.1), частные или защищенные нестатические элементы данных (Раздел 11), нет базовых классов (раздел 10) и нет виртуальных функций (10.3).