1

I've create my own container that is inherited from a vector. I would like to reimplement operator[] in a way that makes checking for bounds decided by a #define.

So putting an example, and ignoring template parmameters as they're complicated and irrelevant

class MyArray : vector<double>
{
    //...
    virtual double& operator[](const size_type& index);
    virtual const double& operator[](const size_type& index) const;
    //...
}


double& MyArray::operator[](const size_type& index)
{
    #ifdef DEBUG_ENABLED
        return this->at(index);
    #else
        return (*this)[index];
    #endif
}

However, this doesn't work because since operator[] is overloaded, calling operator[] at #else would become recursive.

I would like to make the check for bounds based on my #define, and not based on whether I use std::vector<>::at() or std::vector<>::operator[].

How can I resolve this?

EDIT: Since it's offered a lot to use std::vector as a member instead of inheriting, I have to mention that doing that isn't a good solution for me because i'll have to reimplement ALL the member functions of std::vector. That's not so pleasant to do!

The Quantum Physicist
  • 24,987
  • 19
  • 103
  • 189
  • Common implementations of `std::vector` already have this, so you can just keep the `std::vector::operator[]` as is. For example see [here](http://stackoverflow.com/questions/24246017/compile-time-triggered-range-check-for-stdvector). – Baum mit Augen Jun 13 '15 at 14:02
  • Start out with interning the `std::vector` instead of inheriting. – πάντα ῥεῖ Jun 13 '15 at 14:02
  • Instead of `return (*this)[index];` use `return vector::at(index);` – WaeCo Jun 13 '15 at 14:02
  • 1
    @πάνταῥεῖ I would've done that, except that I don't like that because I'll have to rewrite all functions of std::vector. – The Quantum Physicist Jun 13 '15 at 14:04
  • @WaeCo This will force checking bounds anyway. This is wrong. – The Quantum Physicist Jun 13 '15 at 14:04
  • @BaummitAugen I'm checking your solution. Thanks. – The Quantum Physicist Jun 13 '15 at 14:04
  • @BaummitAugen Unfortunately that solution is compiler dependent... my programs are usually cross-platform and this could be a problem. – The Quantum Physicist Jun 13 '15 at 14:09
  • To call `vector::operator[]`, you can use a cast `static_cast&>(*this)[index]` or a qualified-id: `vector::operator[](index)`, possibly using the injected class name `vector::operator[](index)` (not inside a class template, if the `vector` base is dependent). – dyp Jun 13 '15 at 14:10
  • @dyp I tried this now and it crashed my program too, just like the recursive solution. – The Quantum Physicist Jun 13 '15 at 14:13
  • @TheQuantumPhysicist Is that really a problem? You usually only need those checks during development, i.e. on your own system. Your customers can then build the (already debugged/tested) code without range check since they would never fire anyway (if your tests are good enough). – Baum mit Augen Jun 13 '15 at 14:19
  • @BaummitAugen When I write my programs I could do mistakes with bounds and get crashes out of nowhere (and it happened a few days ago), and after spending 2 hours tracking it I realized that I should have had an enable functionality of debugging... I use this container that I created because it contains many functions that make my work easier. – The Quantum Physicist Jun 13 '15 at 14:22
  • @TheQuantumPhysicist I feel like you misunderstood my comment. I fully agree that enabling debug facilities like range checks is a very good idea during development. My intention was to tell you that the cross platform problem does not really exist because you only need the checks on the platforms you develop on, not on the client systems that only get tested and functional code anyway. – Baum mit Augen Jun 13 '15 at 14:25
  • 1
    @TheQuantumPhysicist `I tried this now and it crashed my program too,` Why did your program crash? It couldn't be recursion issue, since the call given to you in the comment calls the base class operator [ ]. See here: http://coliru.stacked-crooked.com/a/2470e03aa0868f05 The runtime error is not due to stack overflow, but due to the index being out of bounds. – PaulMcKenzie Jun 13 '15 at 14:44
  • @TheQuantumPhysicist I lost track of what the problem is. I posted a solution. Let me know if there's a problem with it. – juanchopanza Jun 13 '15 at 14:53

3 Answers3

2

Simply call the base class member functions as required:

double& operator[](size_type index)
{
    #ifdef DEBUG_ENABLED
        return std::vector<double>::at(index);
    #else
        return std::vector<double>::operator[](index);
    #endif
}

You should provide a const version of this too. Also note you probably don't want to make this operator virtual.

juanchopanza
  • 223,364
  • 34
  • 402
  • 480
0

Firstly, it is not a good idea to derive from standard containers, because they lack features (e.g. virtual destructor) that properly supports being used as a base class. The intent of standard containers is that they will not be used as bases.

If you must do this however, add this;

class MyArray : vector<double>
{
    //   all your other stuff

    typedef vector<double> Base;

};

double& MyArray::operator[](const size_type& index)
{
    #ifdef DEBUG_ENABLED
        return this->at(index);
    #else
        return (*((Base *)this))[index];
    #endif
}

The type conversion in the function is often considered better done using a static_cast, but you get the idea.

It is better to make the container a member of your class, and various members of operator functions forward to the contained member.

class MyArray
{
    //   all your other stuff

   private:
       std::vector<double> data;


};

double& MyArray::operator[](const size_type& index)
{
    #ifdef DEBUG_ENABLED
        return data.at(index);
    #else
        return data[index];
    #endif
}

Lastly, either way, the operator[]() need not be virtual.

Peter
  • 35,646
  • 4
  • 32
  • 74
  • 2
    Why would the absence of a virtual destructor matter at all here? – juanchopanza Jun 13 '15 at 14:19
  • A class that is designed or intended to be derived from usually has a number of attributes. Absence of those attributes normally indicates either that the class is intended to not be derived from, or that the implementer did not consider the possibility. A virtual destructor is one of those attributes. – Peter Jun 13 '15 at 22:06
  • 1
    Not really. A virtual destructor is not generally needed for private inheritance. – juanchopanza Jun 13 '15 at 22:44
-1

"Since it's offered a lot to use std::vector as a member instead of inheriting, I have to mention that doing that isn't a good solution for me because i'll have to reimplement ALL the member functions of std::vector. That's not so pleasant to do!

You don't have to. I used inheritance here to save the stress.

#include <iostream>
#include <vector>

using namespace std;

template <typename T>
class Vector_ : private vector<T>
{
public:
    virtual ~Vector_(){}
    virtual const T& operator[](const size_t index);
    virtual const T& at(const size_t index);
};

/*
<<I would like to make the check for bounds based on my #define, and  not based on
std::vector<>::at() or std::vector<>::operator[]. >>
*/
template <typename T>
const T& Vector_<T>::operator[](const size_t index)
{
#ifdef DEBUG_ENABLED
    return (*((vector<T>*)this))->at(index)
#else
    return (*((vector<T> *)this))[index];
#endif
}   
template <typename T>
const T& Vector_<T>::at(const size_t index)
{
#ifdef DEBUG_ENABLED
    return (*((vector<T>*)this))->at(index)
#else
    return (*((vector<T>*)this))[index];
#endif
}

//test...
Vector<int>_ vec;
vec.push_back(3);

vec[2]; or vec.at(2); //no exception

#define DEBUG_ENABLED
vec[2]; or vec.at(2);  //exception
Acha Bill
  • 1,255
  • 1
  • 7
  • 20
  • OP is already using inheritance. But they are sensible and use private inheritance. Public inheritance of std library containers isn't a great idea. Also, there are already working solutions in previous answers. – juanchopanza Jun 13 '15 at 15:37