Reputation: 2449
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
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
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