Nikratio
Nikratio

Reputation: 2449

Container of vectors with fixed, but runtime-determined length in C++

Is there a way to construct a container that contains vectors of the same length, but with this length determined at runtime (i.e. container construction time)?

This question has already been asked as C++: Vector of fixed but runtime defined length arrays with the motivation of saving memory. My motivation is different, I would like to avoid potential bugs without having each function that uses the container first having to verify that all elements are of the same length.

(In my specific case, I am using an std::unordered_map, but I assume that the answer is independent of the container type. I'm also happy to use a custom class instead of unordered_map if that helps).

Upvotes: 3

Views: 226

Answers (2)

jfMR
jfMR

Reputation: 24738

As already suggested in this other answer, you can create a new type based on std::vector (e.g., by relying on object composition), and this new type keeps the invariants you need.

As an example, consider the following fixed_vector class template:

#include <vector>

template<typename T>
class fixed_vector {
   std::vector<T> vec_; // underlying vector
public:
   using size_type = typename std::vector<T>::size_type;

   explicit fixed_vector(size_type count): vec_(count) {}

   fixed_vector(size_type count, const T& value): vec_(count, value) {}

   fixed_vector(std::initializer_list<T> init): vec_(init) {}

   template<typename InputIter>
   fixed_vector(InputIter first, InputIter last): vec_(first, last) {}

   T& operator[](size_type pos) { return vec_[pos]; }
   T const& operator[](size_type pos) const { return vec_[pos]; }

   size_type size() const { return vec_.size(); }

   // ...
};

The size of the std::vector contained in fixed_vector is determined at object construction (i.e., at runtime). Modifying the fixed_vector's size by inserting or deleting an element is not possible since its interface doesn't expose any of the underlying std::vector insert operations (e.g., push_back()) or delete operations (e.g., pop_back()).

// vector of 10 defualt constructed elements
fixed_vector<int> a(10);
assert(a.size() == 10);

// vector of 100 floats with the value 1.5
fixed_vector<float> b(100, 1.5f);
assert(b.size() == 100);

// vector of 3 ints: 1, 2 and 3
fixed_vector<int> c{1, 2, 3};
assert(c.size() == 3);

// initialize from other container
std::vector<int> vec{1, 2, 3, 4, 5};
fixed_vector<int> d(vec.begin(), vec.end());
assert(d.size() == 5);

Note that an assignment operation to a fixed_size object may still change its size, e.g.:

fixed_vector<int> u(100), v(10); // u and v of size 100 and 10, respectively
assert(u.size() == 100); // ok
u = v;
assert(u.size() == 100); // fails! u.size() is now 10

Upvotes: 2

Acorn
Acorn

Reputation: 26066

Yes, of course you can. As usual, the best solution is to create a new type that maintains the invariants you need through its interface.

The easiest would be to simply create a new type for the subcontainers which ensures that the length of your vector is never modified (i.e. does not provide any functions that allow to add or remove elements). Then you can use that type in your container of containers, whichever that is.

Upvotes: 3

Related Questions