简体   繁体   中英

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!

You can add a partial specialization:

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

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) { /* ... */ }
};

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

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; 

};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM