我正在处理的应用程序目前有大量的结构,其中包含从各种来源(如数据库和文件(输入的数据。例如像这样:
struct A
{
float val1;
std::string val2;
int val3;
bool operator < (const A& other) const;
};
为了进行处理,这些结构存储在 STL 容器中,例如映射,因此需要一个比较运算符。这些都是一样的,使用简单的布尔逻辑可以这样写:
bool A:operator < (const A& o) const {
return val1 < o.val1 ||
(val1 == o.val1 && ( val2 < o.val2 ||
(val2 == o.val2 && ( val3 < o.val3 ) ) );
}
这看起来很有效,但有几个缺点:
- 如果结构为十几个或更多成员,则这些表达式会变得很大。
- 如果成员发生变化,编写和维护很麻烦。
- 它需要为每个结构单独完成。
有没有更易于维护的方法来比较这样的结构?
您可以使用<tuple>
附带的内置比较,如下所示:
#include <tuple>
bool A::operator < (const A& rhs) const {
return std::tie(val1, val2, val3) < std::tie(rhs.val1, rhs.val2, rhs.val3);
}
当越来越多的数据成员添加到结构中时,这不会扩展,但这也可能是一个提示,您可以创建实现operator <
的中间结构,从而很好地与顶级operator <
的上述实现配合使用。
让我对operator <
补充三点评论。
一旦你有了
operator <
,客户端就会期望所有其他的比较运算符也被提供。在 C++20 中进行三向比较之前,您可以通过例如使用 Boost 运算符库来避免不必要的样板代码:#include <boost/operators.hpp> struct A : private boost::totally_ordered<A> { /* ... */ };
它根据
operator <
和operator ==
为您生成所有运算符。在您的示例中,运算符不需要是
A
的成员。你可以把它变成一个自由函数,这是更可取的(见这里的基本原理(。如果没有与
A
相关的内部排序,并且您只需要operator <
将实例作为键存储在std::map
中,请考虑提供一个命名谓词。
lubgr的好答案。
我执行的一个进一步改进是在要按其成员排序的任何对象上as_tuple
创建一个成员函数:
#include <string>
#include <tuple>
#include <iostream>
struct A
{
float val1;
std::string val2;
int val3;
// provide easy conversion to tuple
auto as_tuple() const
{
return std::tie(val1, val2, val3);
}
};
这通常会引起对使对象和元组在比较方面可互换的一般系统的想法
template<class T> auto as_tuple(T&& l) -> decltype(l.as_tuple())
{
return l.as_tuple();
}
template<class...Ts>
auto as_tuple(std::tuple<Ts...> const& tup)
-> decltype(auto)
{
return tup;
}
template<class L, class R>
auto operator < (L const& l, R const& r)
-> decltype(as_tuple(l), void(), as_tuple(r), void(), bool())
{
return as_tuple(l) < as_tuple(r);
}
它允许这样的代码:
int main()
{
auto a = A { 1.1, "foo", 0 };
auto b = A { 1.1, "foo", 1 };
auto test1 = a < b;
std::cout << test1 << std::endl;
auto test2 = a < std::make_tuple(1.1, "bar", 0);
std::cout << test2 << std::endl;
auto test3 = std::make_tuple(1.0, "bar", 0) < std::make_tuple(1.1, "bar", 0);
std::cout << test3 << std::endl;
auto test4 = a < std::make_tuple(2l, std::string("bar"), 0);
std::cout << test4 << std::endl;
}
示例:http://coliru.stacked-crooked.com/a/ead750f3f65e3ee9