规范关系运算符(==,<,…)
Canonical relation operators (==,<,...)
考虑一个结构(如:几个成员的愚蠢聚合),其成员都实现了一个特定的关系R
(例如<
):
struct X {
A a;
B b;
};
对于X R X
,大多数算子存在正则定义。例如:
bool operator<(X const& x1, X const& x2) {
if ((x1.a < x2.a) || (x2.a < x1.a)) // I intentionally did not use != here
return x1.a < x2.a;
if ((x1.b < x2.b) || (x2.b < x1.b))
return x1.b < x2.b;
return false;
}
这对于所有操作符来说都是相当无聊的,特别是当你有很多成员而不是只有一个这样的结构体时。
可以看到,operator<
/X
除了使用bool || bool
之外,只依赖于operator<
的成员类型(A
, B
)。
是否有一种方法可以通用地指定这样的操作符(通过模板或内置?)Boost不是一个选项(但如果它能做到这一点,那将是很有趣的)。
如果可以指定成员的求值顺序(为了速度),那就更好了。
编辑这个问题考虑c++ 03,否则你可以使用std::tuple
,我猜。
是boost可以使用tuple。
因此你可以通过模板自己做。但是做这些额外的工作似乎是浪费时间。只要用函数的简单方式来做(尽管我不喜欢你的逻辑)。
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
struct X
{
int a;
float b;
};
标准格式:
#if (V == 1)
// The normal way of doing it.
bool operator<(X const& lhs, X const& rhs)
{
if (lhs.a < rhs.a) {return true;}
if ((lhs.a == rhs.a) && (lhs.b < rhs.b)) {return true;}
// Of course for small structures (as above) it is easy to compress the above
// lines into a single statement quickly.
//
// For larger structures they tend to break it out
// until they get it correct then spend ten minutes
// collapsing it into a single expression.
return false;
}
压缩后的真正正常方式
#elif (V == 6)
// The normal way of doing it.
bool operator<(X const& lhs, X const& rhs)
{
return (
(lhs.a < rhs.a)
|| ((lhs.a == rhs.a) && (lhs.b < rhs.b))
);
}
我曾经喜欢的方式,因为它是清晰的。
#elif (V == 2)
// The way I like doing it because I think it is slightly more readable.
// Though I normally use the one above now.
bool operator<(X const& lhs, X const& rhs)
{
if (lhs.a < rhs.a) {return true;}
if (lhs.a > rhs.a) {return false;}
// If we get here the A are equal
if (lhs.b < rhs.b) {return true;}
if (lhs.b > rhs.b) {return false;}
return false;
}
冗长元组版本
#elif (V == 3)
// A version that will use tupples to do it.
bool operator<(X const& lhs, X const& rhs)
{
typedef boost::tuple<int, float> Comp;
Comp l(lhs.a, lhs.b);
Comp r(rhs.a, rhs.b);
return l < r;
}
短压缩元组版本
#elif (V == 4)
// A version that will use tupples but slightly more compact.
bool operator<(X const& lhs, X const& rhs)
{
return boost::make_tuple(lhs.a, lhs.b) < boost::make_tuple(rhs.a, rhs.b);
}
#endif
显然没有非boost解决方案,我酝酿了一些模板魔法,我张贴作为答案,以防有人有同样的问题;
版本1:显式参数
namespace multioperator {
enum LazyBool {
LB_false = false,
LB_true = true,
LB_undefined
};
template <typename Cmp, typename B> class Operator {
public:
typedef typename Cmp::first_argument_type A;
private:
A const& a1;
A const& a2;
B const& b;
public:
Operator(A const& a1, A const& a2, B const& b)
: a1(a1), a2(a2), b(b) {
}
operator bool() const {
switch (static_cast<LazyBool>(Cmp(a1,a2))) {
case LB_false:
return false;
case LB_true:
return true;
case LB_undefined:
default: // g++ does not understand that we have all branches :(
return static_cast<bool>(b);
}
}
};
template <typename Fn> class BinaryFunctorMonad {
public:
typedef typename Fn::first_argument_type first_argument_type;
typedef typename Fn::second_argument_type second_argument_type;
typedef typename Fn::result_type result_type;
private:
first_argument_type const& a;
second_argument_type const& b;
public:
BinaryFunctorMonad(first_argument_type const& a, second_argument_type const& b)
: a(a), b(b) {
}
operator result_type() {
return Fn()(a,b);
}
};
enum CmpSymmetry {
CS_Symmetric = false,
CS_Asymmetric = true
};
template <typename Cmp, CmpSymmetry asymmetric> class LazyCmp {
public:
typedef typename Cmp::first_argument_type first_argument_type;
typedef typename Cmp::first_argument_type second_argument_type;
typedef LazyBool result_type;
LazyBool operator()(first_argument_type const& a1, second_argument_type const& a2) const {
if (Cmp(a1,a2))
return LB_true;
if (asymmetric && Cmp(a2,a1))
return LB_false;
return LB_undefined;
}
};
template <typename A, typename B> struct MultiLess {
typedef
Operator<
BinaryFunctorMonad<
LazyCmp<
BinaryFunctorMonad<std::less<A> >,
CS_Asymmetric>
>, B>
Type;
};
template <typename A, typename B> struct MultiEqual {
typedef
Operator<
BinaryFunctorMonad<
LazyCmp<
BinaryFunctorMonad<std::equal_to<A> >,
CS_Symmetric>
>, B>
Type;
};
}
template <typename A, typename B> typename multioperator::MultiLess<A,B>::Type multiLess(A const& a1, A const& a2, B const& b) {
return typename multioperator::MultiLess<A,B>::Type(a1,a2,b);
}
template <typename A, typename B> typename multioperator::MultiEqual<A,B>::Type multiEqual(A const& a1, A const& a2, B const& b) {
return typename multioperator::MultiEqual<A,B>::Type(a1,a2,b);
}
// example: multiLess(a1,a2,multiLess(b1,b2,multiLess(c1,c2,false)))
免责声明:我知道BinaryFunctorMonad
有点用词不当,我只是想不出更好的名字。
版本2:inheritance
template <typename A, typename Chain> class MultiComparable {
private:
A const& a;
Chain chain;
public:
typedef MultiComparable MultiComparableT;
MultiComparable(A const& a, Chain chain) : a(a), chain(chain) {}
bool operator<(MultiComparable const& as) {
if (a != as.a)
return a < as.a;
return chain < as.chain;
}
bool operator==(MultiComparable const& as) {
if (a != as.a)
return false;
return chain == as.chain;
}
};
template <typename A, typename Chain> MultiComparable<A,Chain> multiComparable(A const& a, Chain chain) {
return MultiComparable<A,Chain>(a,chain);
}
//example:
struct X : MultiComparable<int,MultiComparable<float,bool> > {
int i;
float f;
X() : MultiComparableT(i,multiComparable(f,false)) {}
}
相关文章:
- 向量范数的c++中的||运算符
- 在 c++ 中对字符串进行操作的关系运算符无法按预期工作
- 为什么在C++中使用关系运算符创建的模板函数不能对字符串正常工作?
- 当关系运算符的含义相同时,为什么结果不同?
- C++使用关系运算符比较字符串
- 阐明关系运算符重载的概念(需要调试帮助)
- 迭代器关系运算符出错(带单独链接和迭代器的自定义哈希表)
- 当我有两个对象时<<如何重载运算符?(有关系)
- 为什么赋值运算符和相等运算符之间没有 1:1 的关系?
- 重载运算符<关系C++
- C++使用 shared_ptr 但调用了对象的关系运算符?
- C++ 字符 - 关系运算符
- 使用关系运算符比较指针意味着什么
- C++ 如何重载关系运算符以检查两个对象是否相同
- 枚举类的关系运算符重载
- C++ STL 关系运算符如何比较堆栈
- 为什么指针类型的内置关系运算符不生成C++的总顺序?
- ostream 插入运算符与其非成员重载之间的关系
- 字符串类关系运算符重载
- 与关系运算符的字符串比较(长度不同)