Копирование-список-инициализация не копируемых типов
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.