Вопрос о том, почему вы должны предоставить operator==
, не совпадает с вопросом о том, почему вы должны предоставить некоторую функцию сравнения .
Что касается последнего, то причина, по которойВы должны предоставить логику сравнения, если поэлементное равенство редко уместно.Рассмотрим, например, структуру POD с массивом char
.Если он используется для хранения строки с нулевым символом в конце, то две такие структуры могут сравнивать неравные на двоичном уровне (из-за произвольного содержимого после нулевых байтов в строках), но при этом логически эквивалентны.
Кроме того,здесь есть все сложности уровня C ++, упомянутые в других ответах, например, особенно сложный вопрос о полиморфном равенстве (вы действительно не хотите, чтобы компилятор выбирал!).
Так что, по сути, ничего хорошего нетвыбор по умолчанию, поэтому выбор остается за вами.
Что касается первого вопроса, который вы буквально задали, почему вы должны указать operator==
?
Если вы определили operator<
иoperator==
, тогда определения операторов в пространстве имен std::rel_ops
могут заполнить все остальное за вас.Предположительно, причина, по которой operator==
необходим, состоит в том, что было бы излишне неэффективно реализовывать его с точки зрения operator<
(тогда требуется два сравнения).Тем не менее, выбор этих двух операторов в качестве основы полностью сбивает с толку, потому что это делает пользовательский код многословным и сложным, а в некоторых случаях гораздо менее эффективным, чем это возможно!-значная функция compare
, такая как std::string::compare
.
Учитывая вариант функции-члена comparedTo
, вы можете затем использовать класс шаблонов Curily Recurring Template, как показано ниже, для обеспечения полного набора операторов:
template< class Derived >
class ComparisionOps
{
public:
friend int compare( Derived const a, Derived const& b )
{
return a.comparedTo( b );
}
friend bool operator<( Derived const a, Derived const b )
{
return (compare( a, b ) < 0);
}
friend bool operator<=( Derived const a, Derived const b )
{
return (compare( a, b ) <= 0);
}
friend bool operator==( Derived const a, Derived const b )
{
return (compare( a, b ) == 0);
}
friend bool operator>=( Derived const a, Derived const b )
{
return (compare( a, b ) >= 0);
}
friend bool operator>( Derived const a, Derived const b )
{
return (compare( a, b ) > 0);
}
friend bool operator!=( Derived const a, Derived const b )
{
return (compare( a, b ) != 0);
}
};
, где compare
- перегруженная функция, например, вот так:
template< class Type >
inline bool lt( Type const& a, Type const& b )
{
return std::less<Type>()( a, b );
}
template< class Type >
inline bool eq( Type const& a, Type const& b )
{
return std::equal_to<Type>()( a, b );
}
template< class Type >
inline int compare( Type const& a, Type const b )
{
return (lt( a, b )? -1 : eq( a, b )? 0 : +1);
}
template< class Char >
inline int compare( basic_string<Char> const& a, basic_string<Char> const& b )
{
return a.compare( b );
}
template< class Char >
inline int compareCStrings( Char const a[], Char const b[] )
{
typedef char_traits<Char> Traits;
Size const aLen = Traits::length( a );
Size const bLen = Traits::length( b );
// Since there can be negative Char values, cannot rely on comparision stopping
// at zero termination (this can probably be much optimized at assembly level):
int const way = Traits::compare( a, b, min( aLen, bLen ) );
return (way == 0? compare( aLen, bLen ) : way);
}
inline int compare( char const a[], char const b[] )
{
return compareCStrings( a, b );
}
inline int compare( wchar_t const a[], wchar_t const b[] )
{
return compareCStrings( a, b );
}
Теперь это механизм .Как это выглядит, чтобы применить его к вашему классу ...
struct Vec3
{
float x, y, z;
};
?
Ну, это довольно просто:
struct Vec3
: public ComparisionOps<Vec3>
{
float x, y, z;
int comparedTo( Vec3 const& other ) const
{
if( int c = compare( x, other.x ) ) { return c; }
if( int c = compare( y, other.y ) ) { return c; }
if( int c = compare( z, other.z ) ) { return c; }
return 0; // Equal.
}
};
Отказ от ответственности: не очень проверенный код…:-)