Почему структура aggreggate может быть инициализирована скобками, но не может быть использована с использованием того же списка аргументов, что и при инициализации скобок?
Кажется, что этот код:
#include <string>
#include <vector>
struct bla
{
std::string a;
int b;
};
int main()
{
std::vector<bla> v;
v.emplace_back("string", 42);
}
можно заставить работать должным образом в этом случае, но это не так (и я понимаю, почему). дающий bla
конструктор решает это, но удаляет совокупность типов, что может иметь далеко идущие последствия.
Это упущение в Стандарте? Или я пропускаю определенные случаи, когда это взрывается у меня на лице, или это просто не так полезно, как я думаю?
2 ответа
Это упущение в Стандарте?
Это считается открытым дефектом в стандарте, обозначаемом как LWG # 2089. Но хорошее решение этого неуловимо.
Фундаментальная проблема заключается в том, что вы не можете использовать волевые списки в скобках. Инициализация списков типов с помощью конструкторов может фактически скрывать конструкторы, так что некоторые конструкторы могут быть недоступны для вызова посредством инициализации списка. Это vector<int> v{1, 2};
проблема. Это создает 2-элемент vector
, а не 1-элементный вектор, чей единственный элемент равен 2.
Из-за этого вы не можете использовать инициализацию списка в общих контекстах, таких как allocator::construct
,
Что приводит нас к:
Я бы подумал, что для этого нужно сделать трюк SFINAE, иначе прибегнуть к скобке init, которая также работает для агрегатов.
Это потребует is_aggregate
тип черта. Которого в настоящее время не существует, и никто не предложил его существования. О, конечно, вы могли бы обойтись с is_constructible
, как говорится в предлагаемом решении вопроса. Но есть проблема с этим: он эффективно создает альтернативу инициализации списка.
Считают, что vector<int>
пример из ранее. {1, 2}
интерпретируется как двухэлементный initializer_list
, Но через emplace
, это будет интерпретироваться как вызов двухцелого конструктора, так как is_constructible
из этих двух элементов будет правдой. И это вызывает эту проблему:
vector<vector<float>> fvec;
fvec.emplace(1.0f, 2.0f);
vector<vector<int>> ivec;
ivec.emplace(1, 2);
Они делают две совершенно разные вещи. в fvec
случай, он выполняет инициализацию списка, потому что vector<float>
не конструируется из двух поплавков. в ivec
случай, он вызывает конструктор, потому что vector<int>
конструируется из двух целых чисел.
Так что вам нужно ограничить инициализацию списка в allocator::construct
работать только если T
это совокупность.
И даже если вы это сделаете, вам придется распространять этот трюк SFINAE во всех местах, где используется косвенная инициализация. Это включает any/variant/optional
"s in_place
конструкторы и огневые точки, make_shared/unique
звонки и т. д., ни один из которых не использует allocator::construct
,
И это не учитывает пользовательский код, где необходима такая косвенная инициализация. Если пользователи не выполняют ту же инициализацию, что и стандартная библиотека C++, люди будут расстроены.
Это сложная проблема, которую нужно решить таким образом, чтобы не разделять API косвенной инициализации на группы, которые допускают агрегаты и группы, которые этого не делают. Есть много возможных решений, и ни одно из них не является идеальным.
23.2.1 / 15.5
T является EmplaceConstructible в X из аргументов, для аргументов ноль или более аргументов означает, что следующее выражение корректно сформировано:
allocator_traits<A>::construct(m, p, args)
23.2.1 / 15
[Примечание: контейнер вызывает
allocator_traits<A>::construct(m, p, args)
построить элемент в р, используя аргументы. Конструкция по умолчанию вstd::allocator
позвоню::new((void*)p) T(args)
, но специализированные распределители могут выбрать другое определение. —Конечная записка]
Таким образом, по умолчанию распределитель использует конструктор, изменение этого поведения может привести к потере обратной совместимости. Вы можете прочитать больше в этом ответе /questions/45182887/pochemu-emplaceback-ne-ispolzuet-ravnomernuyu-initsializatsiyu/45182907#45182907.
Также есть проблема "На пути к более совершенной пересылке" и случайная дискуссия о ее будущем.