Поскольку, по-видимому, решения не для буста, я создал несколько шаблонов магии, которые я публикую в качестве ответа на случай, если у кого-то возникнет такая же проблема;
Версия 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: наследование
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)) {}
}