Агрегатная инициализация не поддерживает доступ к конструктору

Учитывая приведенный ниже пример, я был удивлен, обнаружив, что, несмотря на то, что конструктор по умолчанию был явно удален (или сделан по умолчанию, если на то пошло), агрегатная инициализация оставалась возможной.

#include <iostream>

struct DefaultPrivate
{
      const int n_;
      static const DefaultPrivate& create();

    private:
      DefaultPrivate() = delete;
};

const DefaultPrivate& DefaultPrivate::create()
{
    static DefaultPrivate result{10};
    return result;
}

int main() {
    DefaultPrivate x; //Fails
    DefaultPrivate y{10};//Works
    return 0;
}

Не определена ли в стандарте связь между частным построением по умолчанию (или удаленным) и инициализацией агрегата?

Это имело место как на GCC 6.3, так и на VCC 2017.

Причина, по которой я задаю вопрос, заключалась в том, что я надеялся, что изменение доступа к конструктору по умолчанию предотвратит инициализацию общедоступных агрегатов.


person Werner Erasmus    schedule 27.10.2017    source источник


Ответы (1)


Начиная с C++11, для инициализации списка

Если T является агрегатным типом, выполняется агрегатная инициализация.

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

...

тип класса (как правило, структура или объединение), который имеет

  • ...

  • нет пользовательских, inherited, or explicit (since C++17) конструкторов (explicitly defaulted or deleted constructors are allowed) (since C++11)

  • ...

Это означает, что начиная с C++11 класс с явно удаленными конструкторами по-прежнему считается агрегатным типом, тогда разрешена агрегатная инициализация.

И эффект такой:

Каждый элемент массива direct public base, (since C++17) или нестатический член класса в порядке индекса массива/появления в определении класса инициализируется копированием из соответствующего предложения списка инициализаторов.

Обратите внимание, что для DefaultPrivate y{10}; в приведенном выше процессе конструктор по умолчанию вообще не будет учитываться, тогда тот факт, что он объявлен как delete и private, не будет иметь значения.

Кстати: для DefaultPrivate x; выполняется инициализация по умолчанию,

если T является типом класса non-POD (until C++11), конструкторы рассматриваются и подвергаются разрешению перегрузки для пустого списка аргументов. Выбранный конструктор (который является одним из конструкторов по умолчанию) вызывается для предоставления начального значения для нового объекта;

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

Если вы используете агрегированную инициализацию, например DefaultPrivate x{};, код тоже будет работать нормально; и n_ будет инициализированное значение (а затем ноль инициализирован) как 0.

ПРЯМОЙ ЭФИР

person songyuanyao    schedule 27.10.2017
comment
Да, тип агрегатный, но он агрегатный в частном смысле, или я надеялся, что он будет, иначе я бы не использовал агрегат в заголовке. - person Werner Erasmus; 27.10.2017
comment
@WernerErasmus Ответ изменен. Дело в том, что конструкторы в данном случае рассматриваться не будут, так что это не имеет значения. - person songyuanyao; 27.10.2017