我有一个属性类型为 std::string 的类。我想通过比较属性为类提供一些比较运算符函数,如 <、>、==、<= 和 >=。
我的问题是:任何简单的方法或工具
(1)只写一两个函数,比如一个for operator <(和==),其他的可以自动生成。
(2) 甚至更简单,因为类比较取决于其类型的属性,std::string
其比较功能已经提供。
我有一个属性类型为 std::string 的类。我想通过比较属性为类提供一些比较运算符函数,如 <、>、==、<= 和 >=。
我的问题是:任何简单的方法或工具
(1)只写一两个函数,比如一个for operator <(和==),其他的可以自动生成。
(2) 甚至更简单,因为类比较取决于其类型的属性,std::string
其比较功能已经提供。
在这种情况下,您提供了一个简单的基类,它实现了所有需要的运算符并简单地从它继承:
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<
and operator==
。使用了类似的技巧boost
。
C++20<=>
可能会进一步改善这种情况(见下文)。
std::rel_ops
(在 C++20 中已弃用)std::rel_ops
提供基于<
an的附加操作==
,因此您只需要编写两个运算符。
但是,它将在 C++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 拾取)。
您可以使用std::rel_ops
,但这是错误的。这是一个不属于任何人的工具包的大锤。
通常的方法是定义operator==
和operator<
了解类的细节。剩下的四个运算符可以写成:
a != b
是!(a == b)
a <= b
是!(b < a)
a > b
是b < a
a >= b
是!(a < b)
一种选择是使用 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
};
(如果您可以控制类)正确的方法是使用 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==
and operator<
。但原则上您可以只定义operator!=
and operator>
。自动“推导”其他运算符会很酷,请参阅C++ 中的非常自动的运算符生成器
这是另一个示例,其中只有一个操作员就足够了。(但可能效率低下?)
#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_;}
};