Копирование-список-инициализация не копируемых типов

12.6.1 - Явная инициализация

struct complex {
  complex();
  complex(double);
  complex(double,double);
};

complex sqrt(complex,complex);

complex g = { 1, 2 };  // construct complex(1, 2) 
                       // using complex(double, double) 
                       // and *copy/move* it into g

8.5 Инициализаторы

14 - Инициализация, которая происходит в форме

T x = a;

а также при передаче аргументов, возврату функции, выделении исключения (15.1), обрабатывая исключение (15.3) и совокупный член инициализация (8.5.1) называется копированием-инициализацией. [Заметка: Копирование-инициализация может вызвать ход (12.8). - конечная нота]

15 - Инициализация, которая происходит в формах

T x(a);

T x{a};

а также в новых выражениях (5.3.4), выражения static_cast (5.2.9), преобразования типов функциональных обозначений (5.2.3), а также базы и член инициализаторы (12.6.2) называется прямой инициализацией.

8.5.4 List-initialization [dcl.init.list]

1 - Инициализация списка - это инициализация объекта или ссылки из бит-init-список. Такой инициализатор называется списком инициализаторов, и разделяемые запятыми инициализаторы-предложения списка называются элементы списка инициализаторов. Список инициализаторов может быть пустым. Инициализация списка может возникать при прямой инициализации или инициализации копирования контексты; инициализация списка в контекст прямой инициализации называется инициализацией прямого списка и инициализация списка в контексте копирования-инициализациикопирование списка инициализация.

Проблема с атоматикой

29.6.5 Требования к операциям с атомными типами [atomics.types.operations.req]

#define ATOMIC_VAR_INIT(value) см. ниже

Макрос расширяется до последовательности токенов, подходящей для константы инициализация атомной переменной статической продолжительности хранения тип, который инициализирован-совместим со значением. [Примечание: это может потребоваться инициализация блокировок. - конечная нота] Параллельный доступ к инициализированной переменной, даже посредством атомной операции, представляет собой гонку данных. [Пример:

atomic<int> v = ATOMIC_VAR_INIT(5);

В соответствии с предыдущими разделами, похоже, не должно быть инициализации присваивания без задействованного конструктора-копии, даже если он отклоняется в соответствии с §12.8.31 и §12.8.32, но атомизация определяется как:

29.5 Атомные типы [atomics.types.generic]

atomic() noexcept = default;
constexpr atomic(T) noexcept;
atomic(const atomic&) = delete;
atomic& operator=(const atomic&) = delete;
atomic& operator=(const atomic&) volatile = delete;
T operator=(T) volatile noexcept;
T operator=(T) noexcept;

Нет конструктора-копии!

Часто ATOMIC_VAR_INIT расширяется до выражения скобки для инициализации скобки, но atomic<int> v = {5} по-прежнему является инициализацией присваивания и подразумевает построение копии после прямого построения временного.

Я просмотрел раздел "постоянная инициализация", чтобы увидеть, есть ли лазейка, разрешающая это без копии (из-за "Макрос расширяется до последовательности токенов, подходящей для постоянной инициализации атомной переменной статической продолжительности хранения тип, который инициализирован-совместим со значением" ), но я уже отказываюсь.

Связанные дискуссиях:

http://thread.gmane.org/gmane.comp.lib.qt.devel/8298

http://llvm.org/bugs/show_bug.cgi?id=14486

ИЗМЕНИТЬ

Ответ на цитирование соответствующих стандартных разделов при построении процесса дедукции был бы идеальным.

Заключение

Итак, после приятного ответа Николаса Боласа, забавный вывод состоит в том, что complex g = { 1, 2 } - это копия (это контекст инициализации копирования), которые не копируются (инициализация списка копий разрешается как инициализация с прямым списком), для которых в стандарте предлагается операция копирования (12.6.1: ...and copy/move it into g).

FIX

Запрос Pull: https://github.com/cplusplus/draft/pull/37

Ответ 1

complex g = { 1, 2 };  // construct complex(1, 2) 
                       // using complex(double, double) 
                       // and *copy/move* it into g

Это неверно. И я не говорю, что копия/перемещение будет отменено; Я имею в виду, что копирование или перемещение не будет.

Вы указали 8.5 p14, который определяет T x = a; как инициализацию копии. Это правда. Но затем он определяет, как работает инициализация:

От 8.5, p16:

Семантика инициализаторов такова. Тип назначения - тип инициализированного объекта или ссылки, а тип источника - тип выражения инициализатора. Если инициализатор не является одним (возможно, в скобках) выражением, тип источника не определен.

  • Если инициализатор представляет собой (не заключенный в скобки) бит-init-list, объект или ссылка инициализируется списком (8.5.4).

Это означает, что правила инициализации копирования не применяются к списку с привязкой к init-init. Они используют отдельный набор правил, как описано в 8.5.4.

Вы указали 8.5.4, который определяет T x = {...}; как инициализацию списка копий. Если ваши рассуждения ошибочны, так это то, что вы никогда не искали, что на самом деле выполняет инициализация списка копий. Нет копирования; это то, что он называл.

copy-list-initialization - это подмножество инициализации списка. Поэтому он следует всем правилам, изложенным в 8.5.4, p3. Я не буду приводить их здесь, потому что их несколько страниц. Я просто объясню, как правила применяются к complex g = {1, 2};, чтобы:

  • В списке инициализаторов есть элементы, поэтому это правило не учитывается.
  • complex не является совокупностью, поэтому это правило не учитывается.
  • complex не является специализацией initializer_list, поэтому это правило не учитывается.
  • Применимые конструкторы рассматриваются с помощью разрешения перегрузки в соответствии с правилами 13.3 и 13.3.1.7. Это находит конструктор, который принимает два двойника.

Следовательно, временное создание и копирование/перемещение не будут.

Единственное различие между инициализацией-списком-списком и инициализацией прямого списка указано в 13.3.1.7, p1:

[...] В инициализации списка копий, если выбран явный конструктор, инициализация плохо сформирована.

Это единственное различие между complex g{1, 2} и complex g = {1, 2}. Они оба являются примерами list-initialization, и они работают единообразно, за исключением использования явных конструкторов.

Ответ 2

Конструктор-from-T не является явным, а инициализация списка копий не совпадает с инициализацией копирования. Обе причины "учитывают конструкторы", но копирование-инициализация всегда "рассматривает" копию con & shy; struc & shy; tor, тогда как инициализация списка рассматривает конструкторы с заполненными элементами списка (плюс некоторые детали). К остроумию:

struct Foo
{
    Foo(int) {}
    Foo(Foo const &) = delete;
};

int main()
{
    Foo f = { 1 };  // Fine
}

(Это не сработает, если конструктор был explicit. Кроме того, Foo x = 1;, конечно, завершится с ошибкой из-за конструктора удаленной копии.)

Возможно, еще более просветляющий прецедент:

Foo make() { return { 2 }; }

void take(Foo const &);
take(make());

Все необходимое для этого - в 8.5.4/3 и в 13.3.1.7/1.