Почему нужны копии при инициализации std::vector с помощью std::initializer_list?
#include <initializer_list>
#include <memory>
#include <vector>
struct S {
S() = default;
S(const S&) = delete;
};
struct A {
A(std::initializer_list<std::unique_ptr<S>>);
} a{std::make_unique<S>()}; // 1
std::vector v{std::make_unique<S>()}; // 2
Почему я могу сделать 1, но не могу 2? Читал, что это из-за того, что initializer list создаёт копии, т.к. не может сделать move из const T массива. Но ведь тогда и первый пример бы не работал.
Ответы (2 шт):
Когда вы создаете объект std::vector с помощью std::initializer_list, то необходимо создать копию этого списка. Это происходит потому, что объект std::initializer_list сохраняет ссылки на элементы внутри списка и не владеет ими.
Инициализатор списка (например, {1,2,3}) описывает список значений, которые вы хотите поместить в ваш вектор. При создании вектора с помощью инициализатора списка, например:
std::vector my_vector {1, 2, 3};
std::initializer_list содержит ссылки на те значения, которые вы хотите поместить в ваш вектор. Эти значения находятся вне вектора и не могут быть использованы напрямую.
Чтобы скопировать эти значения внутрь вашего вектора, вам необходимо создать копию этих значений в памяти, которой владеет ваш вектор.
Поэтому при создании вектора с помощью инициализатора списка, каждый элемент списка копируется в отдельную ячейку памяти внутри вектора. Иными словами, при создании std::vector с помощью std::initializer_list, нужны копии, чтобы сохранить значения из инициализатора списка внутрь вектора.
Но ведь тогда и первый пример бы не работал.
А там нет попытки move из константного массива. Константность массива не мешает перемещать в него. (Но в вашем случае даже перемещения не происходит, элемент создается сразу в массиве благодаря mandatory copy elision.)
Есть маленькая библиотечка better_braces, которая это обходит: запустить
#include <better_braces.hpp>
#include <memory>
#include <vector>
struct S {
S() = default;
S(const S&) = delete;
};
std::vector<std::unique_ptr<S>> v = init{std::make_unique<S>()};
// ^~~~
(Я автор.)