实现比较运算符的模板 class
Template class implementing comparison operators
将所有重载的比较运算符写入 class 是我的一项常见任务,因此我编写了一个模板 class 实现了 <,<=,>=,! = 如果派生的 class 实现了 == 和 <。它正在工作,但有很多演员和不太明显 "Curiously recurring template pattern",所以我想知道是否有更简单的解决方案?
template <class Derived>
class Comparable
{
public:
bool operator!=(const Comparable<Derived>& other) {
return !(static_cast<Derived*>(this)->operator==
(*static_cast<const Derived*>(&other)));
}
bool operator<=(const Comparable<Derived>& other) {
return (static_cast<Derived*>(this)->operator==
(*static_cast<const Derived*>(&other)))
|| (static_cast<Derived*>(this)->operator<
(*static_cast<const Derived*>(&other)));
}
bool operator>(const Comparable<Derived>& other) {
return !(static_cast<Derived*>(this)->operator==
(*static_cast<const Derived*>(&other)))
&& !(static_cast<Derived*>(this)->operator<
(*static_cast<const Derived*>(&other)));
}
bool operator>=(const Comparable<Derived>& other) {
return !(static_cast<Derived*>(this)->operator<
(*static_cast<const Derived*>(&other)));
}
};
如果评论中的描述不明显:
template <typename T>
struct Comparable {
friend bool operator!=(T const & lhs, T const & rhs) { return !(lhs == rhs); }
friend bool operator> (T const & lhs, T const & rhs) { return rhs < lhs; }
// ...
};
class MyType : Comparable<MyType> {
int data;
friend bool operator==(MyType const & lhs, MyType const & rhs) {
return lhs.data == rhs.data;
}
friend bool operator< (MyType const & lhs, MyType const & rhs) {
return lhs.data < rhs.data;
}
public:
// ...
};
当编译器遇到 MyType a, b; a > b;
运算符查找时,最终会执行 ADL,它会在 MyType
和 Comparable<MyType>
内部查找(因为这是一个基数),它将在其中找到您需要的实现:bool operator>(MyType const&, MyType const&)
.
作为自由函数的运算符允许在被比较的类型(在本例中为基类)之外进行定义,同时使这些运算符只能通过 ADL 使用(两个参数之一必须是 Comparable<MyType>
).自由函数的使用还提供了类型对称性,编译器将允许在两侧进行隐式转换,而在成员函数的情况下,它只允许在运算符的右侧进行转换。
为了完整起见,可以采用另一种技巧,即在命名空间中将运算符作为模板提供,并提供可用于将该命名空间引入 ADL 的标记:
namespace operators {
template <typename T>
bool operator>(T const & lhs, T const & rhs) {
return rhs < lhs;
}
// rest of the operators come here
struct tag {};
}
class MyType : operators::tag {
int data;
friend bool operator<(T const & lhs, T const & rhs) {
return lhs.data < rhs.data;
}
//...
};
技巧基本相同,只是在这种情况下,运算符不是在基内找到的,而是在与其关联的名称空间中找到的。这个解决方案不如前一个好,因为它容易受到不同形式的滥用,包括 using namespace operators;
这将使模板化运算符可用于 所有 类型。
将所有重载的比较运算符写入 class 是我的一项常见任务,因此我编写了一个模板 class 实现了 <,<=,>=,! = 如果派生的 class 实现了 == 和 <。它正在工作,但有很多演员和不太明显 "Curiously recurring template pattern",所以我想知道是否有更简单的解决方案?
template <class Derived>
class Comparable
{
public:
bool operator!=(const Comparable<Derived>& other) {
return !(static_cast<Derived*>(this)->operator==
(*static_cast<const Derived*>(&other)));
}
bool operator<=(const Comparable<Derived>& other) {
return (static_cast<Derived*>(this)->operator==
(*static_cast<const Derived*>(&other)))
|| (static_cast<Derived*>(this)->operator<
(*static_cast<const Derived*>(&other)));
}
bool operator>(const Comparable<Derived>& other) {
return !(static_cast<Derived*>(this)->operator==
(*static_cast<const Derived*>(&other)))
&& !(static_cast<Derived*>(this)->operator<
(*static_cast<const Derived*>(&other)));
}
bool operator>=(const Comparable<Derived>& other) {
return !(static_cast<Derived*>(this)->operator<
(*static_cast<const Derived*>(&other)));
}
};
如果评论中的描述不明显:
template <typename T>
struct Comparable {
friend bool operator!=(T const & lhs, T const & rhs) { return !(lhs == rhs); }
friend bool operator> (T const & lhs, T const & rhs) { return rhs < lhs; }
// ...
};
class MyType : Comparable<MyType> {
int data;
friend bool operator==(MyType const & lhs, MyType const & rhs) {
return lhs.data == rhs.data;
}
friend bool operator< (MyType const & lhs, MyType const & rhs) {
return lhs.data < rhs.data;
}
public:
// ...
};
当编译器遇到 MyType a, b; a > b;
运算符查找时,最终会执行 ADL,它会在 MyType
和 Comparable<MyType>
内部查找(因为这是一个基数),它将在其中找到您需要的实现:bool operator>(MyType const&, MyType const&)
.
作为自由函数的运算符允许在被比较的类型(在本例中为基类)之外进行定义,同时使这些运算符只能通过 ADL 使用(两个参数之一必须是 Comparable<MyType>
).自由函数的使用还提供了类型对称性,编译器将允许在两侧进行隐式转换,而在成员函数的情况下,它只允许在运算符的右侧进行转换。
为了完整起见,可以采用另一种技巧,即在命名空间中将运算符作为模板提供,并提供可用于将该命名空间引入 ADL 的标记:
namespace operators {
template <typename T>
bool operator>(T const & lhs, T const & rhs) {
return rhs < lhs;
}
// rest of the operators come here
struct tag {};
}
class MyType : operators::tag {
int data;
friend bool operator<(T const & lhs, T const & rhs) {
return lhs.data < rhs.data;
}
//...
};
技巧基本相同,只是在这种情况下,运算符不是在基内找到的,而是在与其关联的名称空间中找到的。这个解决方案不如前一个好,因为它容易受到不同形式的滥用,包括 using namespace operators;
这将使模板化运算符可用于 所有 类型。