Range checking for operator[] with std::vector and std::array in debug mode

140 views Asked by At

I'm writing some numerical C++ where both std::vector and std::array are being used in performance critical parts of the code. Preferably I would like the operator[] to do range checking in debug mode to weed out any potential out of bounds accesses, but provide no overhead in release build.

In principle this could have been easily accomplished with assert statements, but I'm not sure how the stl library code can be edited without doing something bad. I won't consider using the at() method, since this will give overhead also in a release build. I am using g++ to compile. I have read that asserts can be turned on for the MSVC compiler, but I'm using linux and g++, and I don't think this is an option here.

I have created this derived class for the std::vector, it is working fine for my purposes thus far, but I suspect this is not an ideal solution.

template <typename T> class Vector final : public std::vector<T> 
{ 
public: 
    using std::vector<T>::vector; 
#ifndef NDEBUG 
    T &operator[](size_t i) 
    { 
        assert(i < this->size()); 
        return *(this->_M_impl._M_start + i); 
    } 
    const T &operator[](size_t i) const 
    { 
        assert(i < this->size()); 
        return *(this->_M_impl._M_start + i); 
    } 
#endif 
}; 

I was not able to create a similar derived class for the std::array without problems.This post addressed the issue for the std::vector: Compile time triggered range check for std::vector , but seems like inheriting from the std classes is not popular.

One answer from this post also recommends using the flag -D_GLIBCXX_DEBUG, but I found that this check produced errors from another part of the code unrelated to vectors and arrays that was working fine (tried using the yaml-cpp library and accessing a root with the operator[]).

1

There are 1 answers

2
AudioBubble On

You may want:

template <typename T> 
class Vector final : std::vector<T> { ... };

int main() {
    Vector<int> v;

#ifndef NDEBUG
    v[1];    
#else 
    v.at(1);
#endif

    return 0;
}

Or modify the Vector class:

template <typename T> 
class Vector final : std::vector<T> { 
public:
    using std::vector<T>::vector; 
 
    T &operator[](std::size_t i) { 
#ifndef NDEBUG 
        assert(i < this->size()); 
#endif
        return *(this->_M_impl._M_start + i); 
    } 

    const T &operator[](size_t i) const { 
#ifndef NDEBUG 
        assert(i < this->size()); 
#endif
        return *(this->_M_impl._M_start + i); 
    } 
};