Why does vector take an initializer_list in its constructor instead of using a variadic template?

116 Views Asked by At

In this code

vector<std::string> vec = { "long string", "exterminate" };

an initializer_list of std::string is created and each element in copied into vec. This is inefficient and makes it impossible to initialize vectors with move only types. Why can't the constructor perfectly forward the arguments? If that was true you could do this:

vector<unique_ptr<int>> vec = {
    make_unique<int>(5), make_unique<int>(55), make_unique<int>(-4)
};

Instead of this:

vector<unique_ptr<int>> vec;
vec.push_back(make_unique<int>(5));
vec.push_back(make_unique<int>(55));
vec.push_back(make_unique<int>(-4));

Furthermore, if vector cares about copying elements in its constructor, why are you allowed to move elements with push_back?

1

There are 1 best solutions below

2
Roman Leshchuk On

Unfortunately, the behaviour which you want to obtain is impossible using std::vector constructor. It can not use variadic template, because std::vector is a container of single type, so variadic template constructor would not be type-safe. If you really want to construct std::vector container with non-copyable elements, consider using the following function:

template <typename T, size_t S>
std::vector<T> construct(std::array<T, S>&& elems)
{
    std::vector<T> result{};
    result.reserve(elems.size());
    for (T& elem : elems)
    {
        result.push_back(std::move(elem));
    }
    return result;
}

You need to pass std::array to it in this way:

construct(std::array{ make_unique<int>(7), make_unique<int>(67) });

You can simply push_back non-copyable types, because they are not const, while they are const in std::initializer_list. For this reason, the function above is using std::array for this purpose, because this container allows access to its elements by non-constant reference or iterator, which allows non-constant access.