Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a std::vector type-safe

Tags:

c++

stl

I have an obect called an IndexSet, currently defined as a std::vector, that I want to define as a separate type.

I want to be able to interact with it just as though it were a vector, but I also want type protection so that I don't accidentally use a "normal" vector when I want to use an IndexSet.

I have been able to come up with three options for how to do this, none of which please me. I am hoping that there is a fourth that I am missing.

Option #1: typdef

typdef vector<int> IndexSet

This allows me to use an IndexSet exactly as I would a vector, but it gives me zero type protection. I am able to pass a vector into a function expecting an IndexSet with zero complaints.

Option #2: Public Wrapper Class

class IndexSet
{
public:
  vector<int> indexes;
};

This will give me type protection, but it requires me to use a level of indirection interacting with it. Instead of saying set.push_back(1); I have to say set.indexes.push_back(1);

Option #3: Private Wrapper Class

class IndexSet
{
public:
  push_back....
  operator[]...
  etc...
private:
  vector<int> indexes
};

This will give me both type protection and allow me to interact directly with the IndexSet as though it were a vector, but ONLY if I first create wrapper methods for every single method of std::vector that I want to use with my IndexSet.

Of course, what I'd really like to do is to just create a new class that inherits from vector but has zero implementation of its own, but I know that the standard library containers do not like to be inherited from.

Are there any other options that I'm missing?

like image 546
PortMan Avatar asked Oct 01 '22 01:10

PortMan


1 Answers

Is there some functionality that differs between an IndexSet and a vector? Is there some difference in how these objects are used? If the answer is no, then why do you want to do this?

Your typedef does not suffice only if there is something intrinsically wrong with supplying a std::vector<int> to a functions that expects an IndexSet. That would suggest that an IndexSet does not satisfy an is-a relationship with respect to std::vector<int>. That in turn means that even if you could public inheritance, you shouldn't be doing so.

If the relationship is implemented-by rather than is-a, this suggests using either containment or private (and possibly protected) inheritance. This is much safer than public inheritance from a container class because programmers who use your class have to go out of their way to get a base class pointer. (The way to do it is to use a C-style cast. C-style casts can convert a derived type to a parent class even if the inheritance is not public.)

The advantage of using private inheritance in instead of containment in this case is that you can easily promote selected inherited member functions from private to protected via the using statement. You would have to write a bunch of wrapper functions if you used containment.

class IndexSet : private std::vector<int> {
public:
   // Bunch of constructors, elided.

   using std::vector<int>::push_back;
   using std::vector<int>::operator[];
   using std::vector<int>::cherry_picking_of_only_the_stuff_you_want;
};

Update

There are some non-member functions associated with std::vector, specifically comparison operators and std::swap. Making comparable versions for your IndexSet will require wrapper functions, but there aren't that many (six comparison operators plus std::swap), and you only need these if that functionality makes sense for this class.

like image 117
David Hammen Avatar answered Oct 03 '22 14:10

David Hammen