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

→ Ссылка
Автор решения: HolyBlackCat

Но ведь тогда и первый пример бы не работал.

А там нет попытки 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>()};
//                                  ^~~~

(Я автор.)

→ Ссылка