Учитывая следующий фрагмент кода:
class Foo {};
Foo makeFoo() { return Foo{}; }
int main()
{
Foo myFoo{makeFoo()};
}
Я ожидаю, что одна строка в main
объявить и определить / инициализировать myFoo
с помощью Foo
Переместить конструктор на возвращаемое значение makeFoo()
,
Тем не менее, я получаю следующую ошибку от clang++
3.5.1 (компиляция в режиме C ++ 14):
error: excess elements in struct initializer
Foo myFoo{makeFoo()};
^~~~~~~~~
1 error generated.
Что тут происходит? Что конкретно означает «struct initializer» — это просто конструктор по умолчанию (без аргументов) для POD? Почему не вызывается конструктор перемещения?
В конце концов, не существует такого понятия, как «универсальный (или равномерный) синтаксис инициализации». Инициализация списка имеет некоторые особенности поведения.
В вашем случае соответствующие правила находятся в разделе 8.5.1:
Агрегат — это массив или класс (раздел 9) без предоставленных пользователем конструкторов (12.1), без закрытых или защищенных нестатических элементов данных (пункт 11), без базовых классов (пункт 10) и без виртуальных функций (10.3 ).
Ваш class Foo
поэтому является совокупный.
Когда агрегат инициализируется списком инициализаторов, как указано в 8.5.4, элементы списка инициализаторов берутся в качестве инициализаторов для элементов агрегата в порядке возрастания индекса или элемента. каждый
элемент инициализируется копией из соответствующего предложения инициализатора. Если предложение-инициализатор является выражением, и для преобразования выражения требуется сужающее преобразование (8.5.4), программа некорректна.
Вот как компилятор интерпретирует ваш код (как отмечает @chris, в следующей версии C ++ этого не произойдет … хотя я бы сказал, что это правило также необходимо обновить, просто «как указано в 8.5.4 «на самом деле недостаточно, чтобы остановить агрегатное поведение при инициализации).
Поскольку инициализаторов больше, чем членов, это незаконно.
Агрегат, который является классом, также может быть инициализирован одним выражением не заключены в фигурные скобки, как описано в 8.5.
Это правило разрешает инициализацию копирования / перемещения. Копирование / перемещение агрегата не может использовать фигурные скобки.
поскольку Foo
, является агрегатом, выполняется агрегатная инициализация.
N3797 §8.5.4 [dcl.init.list] / 3:
Инициализация списка объекта или ссылки типа T определяется следующим образом:
- Если T является агрегатом, выполняется агрегатная инициализация (8.5.1).
Кажется, это было изменено для C ++ 17, в соответствии с N4296:
Инициализация списка объекта или ссылки типа T определяется как
следующим образом:
- Если T является типом класса и список инициализатора имеет
один элемент типа cv U, где U — это T или класс, производный от T,
объект инициализируется из этого элемента (путем копирования-инициализации
для копирования списка инициализации или прямой инициализации для
прямой список инициализации).