Перегрузка оператора класса шаблона C ++ одинаковыми сигнатурами - PullRequest
0 голосов
/ 09 января 2019

Простой шаблон C ++ OO, изменяющий шаблоны и перегрузка операторов: В следующем классе я дважды перегружал оператор индекса:

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

Теперь, если я создаю экземпляр объекта этого шаблонного класса с теми же типами имен:

test<int, int> obj;

вызов оператора индекса приведет к ошибке, поскольку две перегруженные функции будут иметь одинаковые подписи.

Есть ли способ решить эту проблему?

Извините, если это основной вопрос. Я все еще учусь!

Ответы [ 4 ]

0 голосов
/ 09 января 2019

Вот пример решения, использующего if constexpr, который требует 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; 

};
0 голосов
/ 09 января 2019

Вы можете избежать этой проблемы и сделать код более надежным и выразительным, преобразовав индекс в другой тип, который проясняет, чего хочет пользователь. Использование было бы так:

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

Реализовано так:

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

Теперь Key и Value являются популярными именами, поэтому их использование этими вспомогательными функциями не является лучшим. Кроме того, это всего лишь довольно теоретическое упражнение, потому что функции-члены, конечно, намного лучше подходят для этой задачи:

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) { /* ... */ }
};
0 голосов
/ 09 января 2019

В C ++ 2a вы можете использовать requires, чтобы в некоторых случаях «отбросить» функцию:

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);
};

Демо

0 голосов
/ 09 января 2019

Вы можете добавить частичную специализацию:

template<class A>
class test<A, A>
{
  A a1, a2;
public:
  A& operator[](const A&);
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...