I want to implement a custom vector class myVector<dataT>
which is identical to std::vector
expect that its index starts from an offset which is given as parameter.
Example usage below:
myVector<int> vec(3,0,1); // length=3, initial_value=0, offset=1
assert(vec.size()==3);
vec[1]=1, vec[2]=2, vec[3]=3;
assert(vec[1]==1);
assert(vec[3]==3);
Basically I want to override the dataT& operator[]
method while all other methods remain the same. But I want handle cases where offset
is a variable instead of a constant.
If offset
is constant, I can declare it as template parameter. But this way I cannot pass a variable as offset
.
template <typename dataT, size_t offset>
class myVector{
//definition
};
myVector<int, 1> vec; //valid
int offset=1;
myVector<int, offset> vec; //invalid
I have several possible way to implement myVector
in mind, each requires some boiler-plate code:
Method 1: Inherit std::vector
, accept offset
as parameter of constructor
template <typename dataT>
class myVector : public std::vector {
myVector(int n, int v0, int offset) {}
dataT& operator [] (int index) {return this->at(index-offset);}
}
By inheriting, methods like size, push_back
works automatically. But to accept offset
as parameter of constructor, I have to make changes to all overloaded constructors, which can be verbose.
Method 2: Inherit std::vector
, make offset
a data member.
Similar to method 1, but we don't pass offset
as constructor parameter. Instead, we use setOffset()
to assign value to it.
While this method gets rid of most boilerplate code, every myVector
definition requires a setOffset
, which is also not elegant.
Method 3: std::vector
as data member of myVector
.
template <typename dataT>
class myVector {
std::vector<dataT> stdVector;
myVector(const std::vector<dataT> _stdVector, int offset) {}
}
This way definition/initialization of myVector
becomes simple, but we need to write myVector::size(), myVector::push_back()
, which is also verbose.
So, I have concerns about all 3 methods mentioned above. Is there any elegant implementation that both makes initialization simple and do not involve boilerplate code? Thanks!
CodePudding user response:
Method 4: make offset the first parameter to the constructor, use variadic parameters for the rest, and perfect-forward them.
template<typename ...Args>
myVector(int offset, Args && ...args) : std::vector{std::forward<Args>(args)...}
{
}
This solves the immediate problem of a single implementation for overriding all of std::vector
's constructors.
Now, there are other issues with subclassing std::vector
, and all other C library containers which have been talked about ad-infinitum before. That may or may not be of a concern to you, you should carefully consider the implications of that (the usual objects are a lack of a virtual destructor). But, that wasn't in the scope of the original question...