user8044236
user8044236

Reputation:

Extend std::vector with range checking and signed size_type

I would like to use a class with the same functionality as std::vector, but

The possible options that come to my mind are to

  1. Inherit from std::vector. It is not a good idea, as said in the following question: Extending the C++ Standard Library by inheritance?.
  2. Use composition (put std::vector inside my class) and repeat all the interface of std::vector. This option forces me to think about the current C++ standard, because the interface of some methods, iterators is slightly different in C++ 98,11,14,17. I would like to be sure, that when c++ 20 became available, I can simply use it without reimplementation of all the interface of my vector.

Upvotes: 2

Views: 481

Answers (3)

Klaus
Klaus

Reputation: 25623

An answer more to the underlying problem read from the comment:

For example, I don't know how to write in a ranged-based for way:

for (int i = a.size() - 2; i >= 0; i--) { a[i] = 2 * a[i+1]; }

You may change it to a generic one like this:

std::vector<int> vec1{ 1,2,3,4,5,6};
std::vector<int> vec2 = vec1;

int main()
{
    // generic code
    for ( auto it = vec1.rbegin()+1; it != vec1.rend(); it++ )
    {
       *it= 2* *(it-1);
    }

    // your code
    for (int i = vec2.size() - 2; i >= 0; i--)
    {
        vec2[i] = 2 * vec2[i+1];
    }

    for ( auto& el: vec1) { std::cout << el << std::endl; }
    for ( auto& el: vec2) { std::cout << el << std::endl; }
}

Not using range based for as it is not able to access relative to the position.

Upvotes: 2

stijn
stijn

Reputation: 35901

Regarding point 1: we hardly ever get those warnings here, because we use vectors' size_type where appropriate and/or cast to it if needed (with a 'checked' cast like boost::numeric_cast for safety). Is that not an option for you? Otherwise, write a function to do it for you, i.e. the non-const version would be something like

template<class T>
T& ati(std::vector<T>& v, std::int64_t i)
{
  return v.at(checked_cast<decltype(v)::size_type>(i));
}

And yes, inheriting is still a problem. And even if it weren't you'd break the definition of vector (and the Liskov substitution principle I guess), because the size_type is defined as

an unsigned integral type that can represent any non-negative value of difference_type

So it's down to composition, or a bunch of free functions for accessing with a signed size_type and a range check. Personally I'd go for the latter: less work, as easy to use, and you can still pass your vector to functions teaking vectors without problems.

Upvotes: 1

kebs
kebs

Reputation: 6707

(This is more a comment than a real answer, but has some code, so...)

For the second part (range checking at runtime), a third option would be to use some macro trick:

#ifdef DEBUG
  #define VECTOR_AT(v,i) v.at(i)
#else
  #define VECTOR_AT(v,i) v[i]
#endif

This can be used this way:

std::vector<sometype> vect(somesize);
VECTOR_AT(vect,i) = somevalue;

Of course, this requires editing your code in a quite non-standard way, which may not be an option. But it does the job.

Upvotes: 0

Related Questions