Скопировать elision для инициализации списка, где это указано в стандарте?

В [dcl.init]/17.6 это явно написано, что для случая инициализации в скобках происходит удаление копии:

Если выражением инициализатора является значение prvalue, а версия исходного типа без уточнения cv относится к тому же классу, что и класс назначения, выражение инициализатора используется для инициализации целевого объекта. [ Пример: T x = T(T(T())); вызывает конструктор T по умолчанию для инициализации x. — конец примера ]

А вот в случае list-initialization, к которому не относится предыдущий абзац, я не нашел ничего похожего. См. [dcl.init.list].

Итак, почему в этом случае есть исключение копирования: T x{T(T())}; в соответствии со стандартом С++ 17.


person Oliv    schedule 28.03.2018    source источник
comment
связанный вопрос: stackoverflow.com/questions/68436137/   -  person Fedor    schedule 19.07.2021


Ответы (1)


Согласно действующему проекту, в таком случае исключения копии нет.

Рассмотрим следующий пример:

#include <iostream>
#include <initializer_list>

struct S {
    S() {std::cout << "default\n";}
    S(const S&) {std::cout << "copy\n";}
    S(std::initializer_list<S>) {std::cout << "initializer list\n";}
};

int main()
{
    S s = S{S()};
}

Согласно выпуску основного языка 2137, должен быть выбран конструктор, принимающий std::initializer_list в качестве параметра (Clang может выбрать конструктор копирования или выполнить копирование здесь, что неверно). Таким образом, конструкторы должны учитываться при инициализации такого списка.

Проблема в том, что когда выбран конструктор копирования/перемещения, разумно исключить это копирование/перемещение. На самом деле, проблема основного языка 2327 уже устранил этот недостаток.

person xskxzr    schedule 28.03.2018
comment
Это действительно прискорбно, это решение проблемы с основным языком касается очень особого случая и отключает очень частое исключение копирования, которого все ожидают. Это очень плохо!! Надеюсь, что и GCC, и Clang делают исключение копирования, если нет конструктора списка инициализаторов обработки. - person Oliv; 28.03.2018
comment
Я не понимаю. Кажется, это особый случай для initializer_list, но OP, похоже, спрашивает об общем случае brace-init-list. На самом деле, я думаю, что условия из текста, цитируемого OP, не применимы к вашему примеру, потому что initializer_list<S> не того же типа, что и S. Итог: ваш пример не доказывает, что это правило нельзя применить для brace-init-list. Проблема 2327, по-видимому, также относится к немного другому случаю, а именно к неявному преобразованию, определяемому пользователем. - person Arne Vogel; 29.03.2018
comment
@ArneVogel Я имею в виду, что для общих случаев не может быть исключения копии, иначе частный случай не может быть обработан правильно. Проблема 2327 говорит о более общей проблеме. Подробнее см. в этом ответе. - person xskxzr; 29.03.2018
comment
Просто для документации (поскольку я потерял на этом свое время, я делюсь ею на случай, если вы работаете с комитетом): GCC 7.3 выбирает конструктор initializer_list, если он существует, но выполняет копирование, если нет init.list. конструктор, Clang не выбирает конструктор списка инициализаторов и всегда пропускает конструктор копирования, ICC 18 исключает конструктор копирования, только если не используется инициализация списка, как указано в стандарте, но не выбирает конструктор списка инициализаторов, если он существует, MSVC 19 делает похоже, не реализует prvalue С++ 17. обозреватель компилятора - person Oliv; 30.03.2018