1

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?

TwistedBlizzard
  • 931
  • 2
  • 9

1 Answers1

0

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.

Roman Leshchuk
  • 192
  • 1
  • 7
  • You could use type traits like std::conjunction with std::is_convertible to make the constructor type safe, no? – TwistedBlizzard Jul 08 '23 at 21:20
  • Yes, but variadic templates are made for passing different types to constructor. So the standard doesn't use them in single-type containers. `std::initializer_list` was specially made for this purpose, but this container has some disadvantages, for example, it can not be moved. So in this specific case `std::array` should be used. – Roman Leshchuk Jul 09 '23 at 09:04