Initializing some elements of vector of defined size

Viewed 73

Is there a way to initialize first few elements of a vector after defining the size of the vector like -

vector<int> vec (10);

This doesn't work and produces a compiler error -

vector<int> vec(10) {1,2,3};

For example with arrays we can do the same thing like -

int arr[5] {1,2,3}; // This will initialize the first 3 elements of the array to 1,2,3 and the remaining two to 0.
3 Answers

In short, no. Your can fill out the entire list of things you want to be in the vector:

vector<int> vec{1, 2, 3, 0, 0, 0, 0, 0, 0, 0};

Which will give you a vector of 10 elements.

Or, you can create the vector, then call resize to make it larger (filling the remaining elements with 0):

vector<int> vec{1, 2, 3};
vec.resize(10);

You generally don't need to do this kind of thing to vector though, because unlike array, you can extend vector as needed, after creation:

vector<int> vec{1, 2, 3};
vec.push_back(4);

There isn't a way to do it all in one line like you can with an array. You can use

vector<int> vec{1,2,3};
vec.resize(10);

but that does make the code a little less easy to use. Another option is to wrap that in a function like

template <typename T> 
auto make_sized_vector(std::intializer_list<T> il, std::size_t size = 0)
{
    const auto vec_size = std::max(size, il.size());
    vector<T> vec;            // create vector
    vec.reserve(vec_size);    // allocate all the storage needed
    vec.assign(il);           // assign the elements
    vec.resize(vec_size);     // set the rest to zero
    return vec;
}

and then you can use that like

auto vec = make_sized_vector<int>({1, 2, 3}, 10);

If you are concerned about passing the std::intializer_list by value see why is `std::initializer_list` often passed by value? for why that really isn't a concern.

In case you want to initialize a vector the way you describe, all at once, so that it can become (e.g.) a const member, this is always possible in C++, with just a bit of ugliness and twisting. Let’s say you have a class:

struct SomeClass {
  SomeClass(const std::vector<int> &start, int rest, std::size_t size);
  const std::vector<int> some_vector_;  // This is const!
};

What the constructor could look like:

SomeClass::SomeClass(const std::vector<int> &start, int rest, std::size_t size)
    : some_vector_{[&start, rest, size] {
        std::vector<int> some_vector;
        some_vector.reserve(size);
        some_vector.insert(some_vector.end(), start.begin(), start.end());
        some_vector.insert(some_vector.end(), size - start.size(), rest);
        return some_vector;
      }()} {}

Basically the problem boils down to: How do I do “something procedural” in an initializer list? To which the answer is: You invoke a function that returns the desired type.

To test the construct above:

#include <cstdint>
#include <iostream>
#include <vector>

namespace { /* SomeClass stuff from above goes here. */ }

int main() {
  SomeClass sc{{1, 2, 3}, 0, 10};
  for (int i : sc.some_vector_) std::cout << i << '\n';
}

There are (of course) plenty of ways to make it (slightly) more efficient if needed, such as

  • a templated variadic constructor to create the initial part of the vector,
  • a templated perfect-forwarding constructor to benefit from R-value containers, and
  • as a combined benefit of the above, arbitrary iterable containers as inputs and as the const member.
Related