Подтвердить что ты не робот

Как лучше всего перегрузить оператор <> <=> =, но только написать одну или две функции сравнения?

У меня есть класс с атрибутом типа std::string. Я хотел бы предоставить некоторые операторные функции сравнения, такие как <, > , ==, < =, и >= для класса путем сравнения атрибута.

Мои вопросы таковы: любой простой способ или инструмент для

(1) просто напишите одну или две функции, такие как одна для оператора < (и ==), другие могут быть автоматически сгенерированы.

(2) или даже проще, поскольку сравнение классов зависит от его атрибута типа std::string, функции сравнения которого уже предоставлены.

4b9b3361

Ответ 1

Любопытно повторяющийся шаблон шаблона

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

template <class T>
struct implement_relational_operators{
    friend bool operator<=(const T & a,const T & b){ return a < b || a == b; }
    friend bool operator>(const T &  a, const T & b){ return !(a <= b); }
    friend bool operator!=(const T &  a, const T & b){ return !(a == b);}
    friend bool operator>=(const T &  a, const T & b){ return !(a < b); }
};

template <class T>
struct scalar : public implement_relational_operators<scalar<T> >{
    T value;
    bool operator<(const scalar& o) const { return value < o.value;}
    bool operator==(const scalar& o) const { return value == o.value;}
};

Это не имеет недостатков от std::rel_ops (см. ниже). Однако вам все равно нужно реализовать operator< и operator==. Аналогичный трюк используется boost.

С++ 20 <=> может улучшить ситуацию (см. ниже).

std::rel_ops (возможно, не рекомендуется в С++ 20)

std::rel_ops предоставляет дополнительные операции на основе < a ==, поэтому вам просто нужно написать два оператора.

Однако он будет устаревшим в С++ 20, где a <=> b будет брошен в микс.

Требования

Вам просто нужно равенство (operator=) и меньшее, чем (operator<) оператор сравнения. Остальное можно сгенерировать автоматически с помощью std::rel_ops, поскольку выполняется следующее:

a != b equal to !(a == b)
a <= b equal to (a < b) || (a == b)
a >= b equal to !(a < b)
a >  b equal to !(a <= b)

Обратите внимание, что они менее эффективны, чем писать вручную.

Предупреждение

Однако, так как вам легко предоставить эти операторы, вы должны просто приложить дополнительные усилия и написать их. И у них также есть некоторые недостатки, о которых упоминается R. Martinho Fernandes:

Обратите внимание, что [они] не будут работать должным образом, если вы:

  • добавить using namespace std::rel_ops в каждый контекст, который вы используете для операторов; или
  • Добавить using::operator!=; using::operator<=; using::operator>=; using::operator>; в пространство имен вашего класса (using namespace std::rel_ops в пространстве имен вашего класса не приемлемо, потому что оно не получает ADL).

Ответ 2

Вы можете использовать std::relops, но это было бы неправильно. Это кувалда, которая не принадлежит никому инструментарию.

Обычный подход заключается в определении operator== и operator< со знанием деталей класса. Оставшиеся четыре оператора могут быть записаны с такими:

a != b - !(a == b)

a <= b !(b < a)

a > b b < a

a >= b !(a < b)

Ответ 3

Один из вариантов - использовать CRTP, например, в трюке Barton Nackman:

https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

https://en.wikipedia.org/wiki/Barton%E2%80%93Nackman_trick

// A class template to express an equality comparison interface.
template<typename T> class equal_comparable {
    friend bool operator==(T const &a, T const &b) { return  a.equal_to(b); }
    friend bool operator!=(T const &a, T const &b) { return !a.equal_to(b); }
};

class value_type
 // Class value_type wants to have == and !=, so it derives from
 // equal_comparable with itself as argument (which is the CRTP).
 : private equal_comparable<value_type> {
  public:
    bool equal_to(value_type const& rhs) const; // to be defined
};

Ответ 4

(Если у вас есть контроль над классом), правильный способ сделать это - использовать Boost.Operators http://www.boost.org/doc/libs/1_66_0/libs/utility/operators.htm

#include<cassert>
#include<boost/operators.hpp>

struct A : boost::totally_ordered<A> // implies equality-comparable and less-than-comparable
{
    int val_;
    A(int const& v) : val_{v}{}
    bool operator==(A const& other) const{return other.val_ == val_;}
    bool operator<(A const& other) const{return other.val_ < val_;}
};

int main(){
    A a1{5};
    A a2{7};

    assert(!(a1 == a2)); // user defined operator==
    assert(a1 != a2);    // automatically defined !=
    assert(a1 < a2);     // user defined operator<
    assert(a2 > a1);     // automatically defined >
    assert(a2 >= a1);    // automatically defined >=
    assert(a1 <= a2);    // automatically defined <=
}

Boost.Operators использует CRTP (см. другие ответы), но делает все для вас.

Я только что узнал из других ответов о std::rel_ops, однако мой преждевременный вывод заключается в том, что это не очень полезно. Boost.Operators кажется более мощным и настраиваемым, хотя он немного навязчив.

Обратите внимание, что вам нужно знать, какую из операций выполнить (в данном случае operator== и operator<. Но в принципе вы можете просто определить operator!= и operator>. Было бы здорово, если другие операторы будут автоматически "выведены", см. Очень автоматический генератор операторов в С++


Это еще один пример, когда достаточно одного оператора. (Но, вероятно, неэффективно?)

#include<boost/operators.hpp>

struct A : boost::totally_ordered<A>, boost::equivalent<A>{
    int val_;
    A(int const& v) : val_{v}{}
    bool operator<(A const& other) const{return other.val_ < val_;}
};