Почему `S x ({})` вызывать конструктор по умолчанию только в режиме GCC 7/С++ 1z?

В следующем фрагменте GCC 7 с режимом С++ 1z вызывает конструктор по умолчанию, но GCC/С++ 14 и Clang/С++ 14, С++ 1z вызывают конструктор списка инициализаторов.

Является ли это поведение затронутым изменением С++ 1z (возможно Гарантированное копирование текста?) или ошибка GCC?

#include <cstdio>
#include <initializer_list>

struct S {
  S() { std::printf("DEF "); }      // (1)
  S(std::initializer_list<int> il)  // (2)
    { std::printf("L=%zu ", il.size()); }
};

int main() {
  S x({});
}

Вывод:

Ответы

Ответ 1

Я думаю, что это gcc-ошибка (представленная как 80804). Порядок правил для [dcl.init] в С++ 17:

Если тип назначения является классом класса (возможно, cv-qualit):

  • Если выражение инициализатора является prvalue, а cv-неквалифицированная версия типа источника является тем же классом, что и класс назначения, выражение инициализатора используется для инициализации целевого объекта.

Эта первая пуля не применяется. Выражение инициализатора здесь {}, что даже не является выражением, поэтому у него даже нет cv-неквалифицированного типа для сравнения с S. Эта пуля будет применяться, если бы мы написали S x(S{}).

  • В противном случае, если инициализация является прямой инициализацией или если она является копией-инициализацией, где cv-неквалифицированная версия типа источника является тем же классом, что или производным классом класса назначения, то конструкторы считается. Соответствующие конструкторы перечислены ([over.match.ctor]), а лучший выбирается с помощью разрешения перегрузки. Выбранный таким образом конструктор вызывается для инициализации объекта с выражением инициализатора или списком выражений в качестве аргумента (ов). Если конструктор не применяется или разрешение перегрузки неоднозначно, инициализация плохо сформирована.

Это прямая инициализация, поэтому конструкторы рассматриваются как [над .match.ctor], что просто говорит о перегрузке конструкторов. Поскольку существует конструктор std::initializer_list, он получает приоритет на [over.ics.rank], так что он выбран.


Единственное различие между С++ 14 и С++ 17 здесь - введение этой первой пули, которая в любом случае не применяется, поэтому поведение должно быть одинаковым.