Search code examples
c++vectoroverloadingsubscript-operator

Overloading subscript operator in an user defined class C++


Consider the following class:

class SocialPrefNode{

public:

// Constructors & Destructor
SocialPrefNode( );
SocialPrefNode( char self, int ind, int link, bool stack, std::vector<SocialPrefNode*> pref,
                std::vector<SocialPrefNode*> worse, std::vector<SocialPrefNode*> indiff );

SocialPrefNode( const SocialPrefNode& copy );

~SocialPrefNode( );

// Setters
void set_id( char self );

void set_index( int ind );
void set_lowlink( int link );

void set_onstack( bool stack );

void set_pref( std::vector<SocialPrefNode*> prefs );
void set_pref( SocialPrefNode& prefs );

void set_worse( std::vector<SocialPrefNode*> wrs );
void set_worse( SocialPrefNode& wrs );

void set_indiff( std::vector<SocialPrefNode*> indiff );
void set_indiff( SocialPrefNode& indiff );

// Getters
char get_id( ){ return id; }

int get_index( ){ return index; }
int get_lowlink( ){ return lowlink; }

bool get_onstack( ){ return onstack; }

std::vector<SocialPrefNode*> get_preferences( ){ return preferences; }
std::vector<SocialPrefNode*> get_worse( ){ return worsethan; }
std::vector<SocialPrefNode*> get_indiff( ){ return indifference; }

// Operators
SocialPrefNode& operator=( const SocialPrefNode& copy );

private:

char id{ };

int index{ };
int lowlink{ };

bool onstack{ };

std::vector<SocialPrefNode*> preferences{ };
std::vector<SocialPrefNode*> worsethan{ };
std::vector<SocialPrefNode*> indifference{ };
};

std::ostream& operator<<( std::ostream& os, SocialPrefNode& node );

Question: Is there a way to overload/override/redefine the subscript operator s.t. one can, for example, have access to a vector of choice, among the three options.

I.e., suppose: SocialPrefNode ordering{ }. I want to be able to use the subscript operator as in ordering[ i ] AND be able to choose one, among the three vectors in the class, for the subscript/index i to act up on.

Example: one wants to access the third element in the preferences vector of a SocialPrefNode ordering. Then, one does ordering[ 2 ] and, thus, have access to the desired element.


Solution

  • A possible solution is to use phantom types to wrap up the vector to use. I followed this blog post and came up with the following solution.

    // Wrapper need once in the complete project:
    template <typename T, typename Parameter>
    class NamedType
    {
    public:
        explicit NamedType(T const& value) : value_(value) {}
        explicit NamedType(T&& value) : value_(std::move(value)) {}
        T& get() { return value_; }
        T const& get() const { return value_; }
    private:
        T value_;
    };
    
    // Only showing the new parts in your class!
    class SocialPrefNode {
    public:
        // One phantom type for each array index
        using preferences_index = NamedType<std::ptrdiff_t, struct preferences_Parameter>;
        using worsethan_index = NamedType<std::ptrdiff_t, struct worsethan_Parameter>;
        using indifference_index = NamedType<std::ptrdiff_t, struct indifference_Parameter>;
    
        // One operator[] for each array index type
        SocialPrefNode* operator[](preferences_index i) { return preferences[i.get()]; }
        SocialPrefNode* operator[](worsethan_index i) { return worsethan[i.get()]; }
        SocialPrefNode* operator[](indifference_index i) { return indifference[i.get()]; }
    }
    
    // Usage is simple assuming a SocialPrefNode s.
    s[0]; // This will not work!
    s[SocialPrefNode::preferences_index{ 0 }];  // Accessing preferences
    s[SocialPrefNode::worsethan_index{ 0 }];    // Accessing worsethan
    s[SocialPrefNode::indifference_index{ 0 }]; // Accessing indifference
    

    If you don't mind adding an external dependency, you can directly use the Github repo of the author of NamedType (where the class is a bit extended in comparison to here).