оператор <переопределение в std :: pair в шаблоне класса - PullRequest
0 голосов
/ 25 апреля 2020

У меня есть следующая ситуация в случае шаблона класса:

template<class T1,class T2>
class targetClass{

public:

    typedef typename std::pair<T1, T2> ToSortType;
    typedef typename std::set<ToSortType> ContainerSort;  

    void bar(ToSortType a, ToSortType b);

private:

    ContainerSort container;
    bool operator<(const ToSortType& rhs) const;

}

template<class T1,class T2>
void targetClass<T1,T2>::bar(ToSortType a, ToSortType b){

    container.insert(a);
    container.insert(b);

}

template <class T1,class T2>
bool targetClass<T1,T2>::operator<(const ToSortType& rhs) const
{
    return this->first < rhs.first;
}

В основной функции что-то вроде этого:

targetClass<int,T2> anObjectTarget;
T2 a;
T2 b;
anObjectTarget.bar(std::make_pair(0,a),std::make_pair(1,b));

Где T2 определяется пользователем тип, который обычно не имеет определенного operator<. В этом конкретном случае std::set должен сравнить std::pair<int,T2>, сначала проверив operator< (и, возможно, другие) для типа int, а затем для T2. В этом случае компилятор не может найти подходящий оператор для T2. В предыдущем фрагменте я делаю переопределение соответствующего оператора, но компилятор жалуется следующим образом:

/usr/include/c++/7/bits/stl_pair.h:456: error: no match for ‘operator<’ (operand types are ‘const T2’ and ‘const T2’)
       || (!(__y.first < __x.first) && __x.second < __y.second); }
                                       ~~~~~~~~~~~^~~~~~~~~~~~  

Я никогда раньше не переопределял оператор, но, глядя на документацию, он выглядит правильным для меня (но не компилятору).

1 Ответ

1 голос
/ 25 апреля 2020

Перегруженный вами оператор является членом targetClass<T1,T2> и принимает два ToSortType в качестве параметра. Это не то, как работает перегрузка оператора <. Учтите, что для экземпляров типа класса следующие два эквивалентны:

 a < b
 a.operator<(b)

ie операторы просто syntacti c sugar для вызова специальных функций-членов. Оператор, который вы написали, может быть вызван только как

 targetClass<T1,T2> t;
 T1 a;
 T2 b;
 t.operator<(a,b);

, но набор пытается вызвать это a < b, ie a.operator(b) и, по-видимому, не существует (std::pair<T1,T2> можно только комарировать через <, когда могут T1 и T2).

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

Я бы не рекомендовал чтобы попытаться перегрузить operator< для std::pair<T1,T2>, но лучше использовать пользовательский тип:

template<class T1,class T2>
class targetClass{

public:
    struct value_type {
        T1 first;
        T2 second;
        bool operator<(const value_type& other) {
             return first < rhs.first;
        }
    }
    using container_type = std::set<value_type>;    
    void bar(const value_type& a,const value_type& b);    
private:    
    container_type container;    
};

Если вы хотите остаться с std::pair, вы можете использовать тот факт, что std::set позволяет вам выбрать тип компаратора. Однако сначала я должен немного объяснить, чтобы не смущать вас, потому что следующее может показаться противоречащим вышесказанному (это не так). Используемый набором компаратора по умолчанию является std::less<Key>, то есть тип с operator(), который сравнивает два элемента типа Key, это что-то похожее (но не совсем), подобное этому:

template <typename Key>
struct less {
    bool operator() (const Key& a,const Key& b) const {
        return a < b;
    }
};

И это место, где компилятор не может найти < для вашего Key типа (который является std::pair<T1,T2>). Вы можете использовать свой собственный компаратор:

template <typename T1,typename T2>
struct my_comparator {
     bool operator() (const std::pair<T1,T2>& a, const std::pair<T1,T2>& b) const {
         return a.first < b.first;
     }
};

И тогда ваш набор будет

using container_type = std::set<typename std::pair<T1,T2>,typename my_comparator<T1,T2>>;
...