G CC здесь правильно; Инициализация списка не позволяет исключить копирование в C ++ 17.
Если вы сделали S s2(S{});
, это потребуется только для вызова конструктора S
по умолчанию из-за [dcl .init] /17.6.1:
Если выражение инициализатора является prvalue, а версия исходного кода cv-unqualified является тем же классом, что и класс назначения, инициализатор выражение используется для инициализации объекта назначения. [Пример: T x = T (T (T ())); вызывает конструктор T по умолчанию для инициализации x. - конец примера]
Однако это относится только к инициализации копии и прямой инициализации.
Выполнение S s2{S{}};
- это инициализация списка, которая является его собственной совершенно отдельной формой инициализации с свои правила. Поскольку S
не является агрегатом, [dcl.init.list] 3.6 вступит во владение, что говорит:
В противном случае, если T является типом класса, конструкторы считается. Применимые конструкторы перечисляются, и лучший выбирается с помощью разрешения перегрузки ([over.match], [over.match.list]).
Вызов конструктора означает вызов определенной функции c с указанным c набором параметров. А это значит, что значение prvalue S{}
должно использоваться для инициализации параметра в выбранном конструкторе. Это означает, что у вас есть для вызова конструктора копирования / перемещения.
Обычные, не гарантированные, исключения также не допускаются. [class.copy.elision] / 1 дает 3 обстоятельства, при которых разрешено допущение: return localVariableName
, throw localVariableName
и catch(TypeName)
, если улов соответствует броску. Этот случай, очевидно, не является ни одним из них, поэтому он не подходит для регулярного исключения.