Перенаправленное построение на месте и инициализация списка

Под пересылаемой конструкцией на месте я понимаю std::allocator::construct и различные методы emplace, например, std::vector::emplace_back< /а>. Я просто обнаружил, что перенаправленная конструкция на месте в С++ не использует (не может?) Синтаксис инициализации списка. В результате кажется, что никогда нельзя пересылать агрегат на месте. Я просто хочу убедиться, что переадресованная конструкция на месте не поддерживает инициализацию списка и, следовательно, агрегатные типы. Это связано с ограничением языка? Может ли кто-нибудь дать ссылку на стандарт по этому вопросу? Ниже приведена иллюстрация:

Хотя мы можем выполнять строительство на месте напрямую, как

int(*p)[3] = ...;
new(p) int[3]{1, 2, 3};

мы не можем выполнять переадресацию на месте, например

std::allocator<int[3]> allo;
allo.construct(p, 1, 2, 3);

person Lingxi    schedule 10.04.2015    source источник
comment
Каков твой вопрос?   -  person R Sahu    schedule 10.04.2015
comment
что-то вроде Type object((foo, bar), anotherFoo, anotherBar);?   -  person user3528438    schedule 10.04.2015
comment
Мое первоначальное утверждение по вопросу неясно. Обновлено сейчас.   -  person Lingxi    schedule 10.04.2015
comment
Вы пытались написать конструктор пересылки, который использует {}?   -  person Yakk - Adam Nevraumont    schedule 10.04.2015
comment
@Yakk Работает на GCC и clang. См. coliru.stacked-crooked.com/a/833a11295113578b. Но я не уверен, действительно ли это разрешено стандартом. Если это разрешено, я не вижу смысла в стандартной библиотеке использовать то, что она использует сейчас, вместо этого.   -  person Lingxi    schedule 10.04.2015
comment
std::allocator::constructstd::allocator_traits::construct) указаны для использования (), а не {}, и должны оставаться такими по причинам совместимости (vector<int>(10, 10) против vector<int>{10, 10}). Есть проблема LWG, которую нужно решить он возвращается к использованию {}, если () не работает.   -  person T.C.    schedule 10.04.2015
comment
@Т.С. Я понимаю вашу точку зрения. Это убедительно. Откат к {} в некоторых случаях все еще может удивить программистов :)   -  person Lingxi    schedule 10.04.2015


Ответы (2)


Хотя {} называют единым синтаксисом инициализации, он далеко не универсален.

возьмем эти два примера:

size_t a = 3;
size_t b = 1;
std::vector<size_t> v1{a,b};
std::vector<size_t> v2(a,b);

в первом случае мы строим вектор, содержащий два элемента, 3 и 1.

Во втором случае мы создаем вектор, содержащий 1,1,1 -- 3 копии 1.

живой пример.

Таким образом, построение на основе {} может в некоторых случаях вызывать поведение, отличное от построения на основе (). Более того, в приведенном выше случае нет способа достичь синтаксиса «3 копии 1», используя конструкцию {} (о которой я знаю). Но случай {3,2} можно обработать, просто явно создав список инициализаторов и передав его в ().

Поскольку большинство типов, которые принимают списки инициализаторов, могут быть созданы путем явной передачи списка инициализаторов, а стандартная библиотека C++ была разработана для типов с конструкторами в большей степени, чем для типов без них, стандартная библиотека C++ почти всегда размещает конструкции с использованием (), а не {}.

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

Теоретически к каждому интерфейсу можно добавить list_emplace методов, которые строятся с использованием {}. Я призываю вас предложить это!

person Yakk - Adam Nevraumont    schedule 10.04.2015
comment
Вам также придется добавить list_construct к распределителям. Не уверен, что мне нравится это направление. - person T.C.; 10.04.2015
comment
@Т.С. Я видел другое направление (где construct возвращается к построению списка, если построение скобок не удается). Это кажется подверженным ошибкам: emplace(1,2) поставить 2, а emplace(1,2,3) поставить 1,2,3 кажется полным безумием. - person Yakk - Adam Nevraumont; 10.04.2015
comment
Неплохо подмечено. Я полагаю, что аналогичное обсуждение семантики инициализации списка имело место во время процесса стандартизации С++ 11, что привело к тому, что конструктор initializer_list всегда предпочтительнее для правила {}. - person T.C.; 10.04.2015
comment
Я думаю, было бы неплохо автоматически переключаться между {} и () в зависимости от того, является ли предоставленный тип агрегатом или нет. - person Lingxi; 10.04.2015
comment
@Yakk emplace и list_emplace разные по имени. Это делает невозможным написание универсального кода шаблона с использованием унифицированного имени метода. - person Lingxi; 10.04.2015
comment
@Yakk Ну, я просто понимаю, что такой общий код, если его можно написать, будет иметь запутанное поведение :( - person Lingxi; 10.04.2015
comment
@Lingxi Ага. Универсального способа инициализации данных в C++ не существует, несмотря на попытки C++11. - person Yakk - Adam Nevraumont; 10.04.2015
comment
невозможно получить 3 копии 1 синтаксиса, используя конструкцию {} Является ли std::vector<int>{3, 1, std::allocator<int>{}} читерством? ;) - person Casey; 10.04.2015

construct() std::allocator (а также реализация по умолчанию, предоставляемая std::allocator_traits) указаны для использования (): ::new((void *)p) U(std::forward<Args>(args)...) (см. [allocator.members]/p12, [allocator.traits.members]/p5).

На данный момент нецелесообразно менять его на {}, потому что это автоматически нарушит существующий код:

std::vector<std::vector<int>> foo;
foo.emplace_back(10, 10); // add a vector of ten 10s with (); two 10s with {}

Существует проблема LWG, которую нужно решить он возвращается к использованию {}, если () не работает. Мы должны увидеть, согласен ли комитет с этим направлением.

@Yakk указывает на потенциальные недостатки этого подхода:

foo.emplace_back(10); // ten 0s
foo.emplace_back(10, 10); // ten 10s
foo.emplace_back(10, 10, 10); // three(!) 10s

Аналогичная проблема (см. Приложение B к N2215) привело к решению, что инициализация списка всегда будет отдавать предпочтение конструкторам initializer_list.

person T.C.    schedule 10.04.2015
comment
Действительно (не зная об этой проблеме), я реализовал этот запасной вариант сам. . Он даже не ломает код. - person Columbo; 12.09.2015