0

In C++, for a vector v, v.at(x) behaves like v[x], except that it throws an out of bounds error if a non-existent element is accessed.

I would like to ideally always use v.at(x), however, it is not as convenient to write as v[x]. Is there a way I can make v[x] behave like v.at(x), perhaps using something like #define?

If not, is there a better solution for out-of-bounds errors to always be thrown?

Mahathi Vempati
  • 1,238
  • 1
  • 12
  • 33
  • 3
    "Using something like `#define`" should always be your last resort. You probably have to write a custom container for that, because`operator[]` can not be overloaded outside the class. – Lukas-T Sep 14 '20 at 11:45
  • 4
    "[I]s there a better solution for out-of-bounds errors"...? Yes, proper validation of all indexes. Especially indexes that are derived (or come directly) from user-input. Together with code-review and thorough unit-testing it should (theoretically) catch all problems. – Some programmer dude Sep 14 '20 at 11:46
  • There are implementations of C++ that choose to have `[]` behave identically to `at`, you could use one of those. – Caleth Sep 14 '20 at 11:48
  • 4
    Some implementations of the C++ Standard Library support bound checking in a debug mode. See, for instance, [GCC STL bound checking](https://stackoverflow.com/q/5594686/580083) or [How to make `std::vector`'s `operator[]` compile doing bounds checking in DEBUG but not in RELEASE](https://stackoverflow.com/q/1290396/580083). – Daniel Langr Sep 14 '20 at 11:48
  • 1
    I use [assert](https://en.cppreference.com/w/cpp/error/assert) a lot. You can use it liberally because it has zero runtime overhead in a release build. – Galik Sep 14 '20 at 11:50
  • 5
    Use iterators (and, C++11 and later) range-based loops, consciously minimise usage of vector indices, and - if you must use indices - validate the indices properly. You'll find that there are numerous cases where vector indices are avoidable - with a bit of thought. – Peter Sep 14 '20 at 11:53
  • @Peter Agree, but the big advantage of indices over iterators is that they remain valid after reallocations, which is useful sometimes. – Daniel Langr Sep 14 '20 at 11:55
  • @DanielLangr - I'm certainly not suggesting to never use indices - they have their uses, as you say. One of their big disadvantages, however, is that a previously validated index can become invalid - and, worse, not necessarily with testable symptoms - if a vector size is reduced, or if some set of its elements are copied to another vector and the indices used on that other vector. Those are just two of the cases that, if indices are being used, call for special care in ensuring indices remain valid - and programmers too often forget to revalidate in such cases – Peter Sep 14 '20 at 12:43

2 Answers2

3

You may consider overloading the method in a new inheritance, as follows

#include <iostream>
#include <vector>

template< class T, class allocator =  std::allocator<T>>
struct Vector : std::vector<T, allocator>{
    using std::vector<T, allocator>::vector;

    const T& operator[](size_t i)const{
        return this -> at(i);
    }

    T& operator[](size_t i){
        return this -> at(i);
    }
};

template< class T>
Vector(size_t, T ) -> Vector<T>;//if u want to use c++17 deduction guides

int main()
{
    std::vector<int> vec1(4,1);
    std::cout << vec1[4];

    Vector vec2(4,1);
    std::cout << vec2[4];

}

asmmo
  • 6,922
  • 1
  • 11
  • 25
3

You can use a proxy object with operator [] that calls method at(). Of course, this is not exactly what you want, but the syntax is similar: instead of v[x] you have to write at(v)[x].

Proof of concept:

#include <iostream>
#include <vector>


template<class T>
class AtProxy
{
  public:
    AtProxy(const AtProxy<T>& proxy) : m_obj{proxy.m_obj} {}
    AtProxy(T& obj) : m_obj{obj} {}
    typename T::reference operator [](size_t index)
    {
      return m_obj.at(index);
    }
  private:
    T& m_obj;
};


template<class T>
AtProxy<T> at(T& v)
{
  return AtProxy<T>(v);
}


int main()
{
  try
  {
    std::vector<int> vec = {1,2,3,4,5};

    for(size_t i = 0; i < 5; i++)   
    {
      std::cout << i << std::endl;
      at(vec)[i] = at(vec)[i + 1] + 1;
    }   
  }
  catch(std::exception& e)
  {
    std::cout << "exception: " << e.what() << std::endl;
  }
   
  std::cout << "ok" << std::endl;
  return 0;
}
B0FEE664
  • 191
  • 5