user3112666
user3112666

Reputation: 1729

C++ template class operator overloading with the same signatures

A simple C++ OO question regrading templates and operator overloading: In the following class, I have overloaded the index operator twice:

template<class A, class B>
class test
{
  A a1;
  B a2;
public:
  A& operator[](const B&);
  B& operator[](const A&);
};

Now, if I instantiate an object of this template class with the same typenames:

test<int, int> obj;

calling the index operator will result in an error, because the two overloaded functions will have the same signatures.

Is there any way to resolve this issue?

Sorry, if this is a basic question. I am still learning!

Upvotes: 5

Views: 479

Answers (4)

tmaric
tmaric

Reputation: 5477

Here is an example solution using if constexpr that requires C++17:

#include <type_traits>
#include <cassert>
#include <string>

template <class A, class B> 
class test
{
  A a1_;
  B b1_;

public:

    template<typename T> 
    T& operator[](const T& t)
    {
        constexpr bool AequalsB = std::is_same<A,B>(); 
        constexpr bool TequalsA = std::is_same<T,A>();

        if constexpr (AequalsB)
        {
            if constexpr (TequalsA) 
                return a1_;  // Can also be b1_, same types;

            static_assert(TequalsA, "If A=B, then T=A=B, otherwise type T is not available.");
        }

        if constexpr (! AequalsB)
        {
            constexpr bool TequalsB = std::is_same<T,B>();

            if constexpr (TequalsA)
                return a1_; 

            if constexpr (TequalsB)
                return b1_; 

            static_assert((TequalsA || TequalsB), "If A!=B, then T=A || T=B, otherwise type T is not available.");
        }
    }

};

using namespace std;

int main()
{
    int x = 0;  
    double y = 3.14; 
    string s = "whatever"; 

    test<int, int> o; 
    o[x]; 
    //o[y]; // Fails, as expected.
    //o[s]; // Fails, as expected

    test<double, int> t; 
    t[x]; 
    t[y]; 
    //t[s]; // Fails, as expected.

    return 0; 

};

Upvotes: 1

Jarod42
Jarod42

Reputation: 217145

In C++2a, you might use requires to "discard" the function in some case:

template<class A, class B>
class test
{
    A a1;
    B a2;
public:
    A& operator[](const B&);
    B& operator[](const A&) requires (!std::is_same<A, B>::value);
};

Demo

Upvotes: 1

Max Langhof
Max Langhof

Reputation: 23681

You can avoid this issue and make the code more robust and expressive by converting the index to some other type that clarifies what the user wants. Usage would be like this:

bidirectional_map<int, int> myTest;
int& valueFor1 = myTest[Key{1}];
int& key1 = myTest[Value{valueFor1}];

Implemented like this:

template<class TKey>
struct Key { const TKey& key; };

template<class TValue>
struct Value { const TValue& value; };

// Deduction guides (C++17), or use helper functions.
template<class TValue>
Value(const TValue&) -> Value<TValue>;
template<class TKey>
Key(const TKey&) -> Key<TKey>;

template<class TKey, class TValue>
class bidirectional_map
{
  TKey a1;   // Probably arrays
  TValue a2; // or so?
public:
  TValue & operator[](Key<TKey> keyTag) { const TKey & key = keyTag.key; /* ... */ }
  TKey & operator[](Value<TValue> valueTag) { const TValue& value = valueTag.value; /* ... */ }
};

Now, Key and Value are popular names so having them "taken up" by these auxiliary functions is not the best. Also, this is all just a pretty theoretical exercise, because member functions are of course a much better fit for this task:

template<class TKey, class TValue>
class bidirectional_map
{
  TKey a1;   // Probably arrays
  TValue a2; // or so?
public:
  TValue& getValueForKey(const TKey& key) { /* ... */ }
  TKey& getKeyForValue(const TValue& value) { /* ... */ }
};

Upvotes: 2

xskxzr
xskxzr

Reputation: 13040

You can add a partial specialization:

template<class A>
class test<A, A>
{
  A a1, a2;
public:
  A& operator[](const A&);
};

Upvotes: 5

Related Questions