结构的高效且简单的比较运算符

Beg*_*ner 19 c++

我正在处理的应用程序目前有大量的结构,其中包含从各种来源(如数据库和文件)输入的数据.例如这样:

struct A 
{
    float val1;
    std::string val2;
    int val3;

    bool operator < (const A& other) const;
};
Run Code Online (Sandbox Code Playgroud)

对于处理,这些结构存储在STL容器中,例如地图,因此需要比较运算符.这些都是一样的,使用简单的布尔逻辑,它们可以像这样写:

bool A:operator < (const A& o) const {
    return val1 < o.val1 || 
        (val1 == o.val1 && ( val2 < o.val2 || 
            (val2 == o.val2 && ( val3 < o.val3 ) ) );
}
Run Code Online (Sandbox Code Playgroud)

这似乎很有效,但有几个缺点:

  1. 如果结构为十几个或更多成员,这些表达式会变得很大.
  2. 如果成员改变,编写和维护是很麻烦的.
  3. 需要分别对每个结构进行操作.

是否有更可维护的方法来比较像这样的结构?

lub*_*bgr 25

您可以使用附带的内置比较,<tuple>如下所示:

#include <tuple>

bool A::operator < (const A& rhs) const {
    return std::tie(val1, val2, val3) < std::tie(rhs.val1, rhs.val2, rhs.val3);
}
Run Code Online (Sandbox Code Playgroud)

当结构中添加越来越多的数据成员时,这不会扩展,但这也可能暗示您可以创建实现的中间结构,operator <因此可以很好地使用顶层的上述实现operator <.

让我再补充三条评论operator <.

  1. 一旦拥有operator <,客户将期望也提供所有其他比较运算符.在我们在C++ 20中进行三向比较之前,您可以通过使用Boost运算符库来避免不必要的样板代码:

    #include <boost/operators.hpp>
    
    struct A : private boost::totally_ordered<A> { /* ... */ };
    
    Run Code Online (Sandbox Code Playgroud)

    其基于所有的运营商operator <,并operator ==为您服务.

  2. 在您的示例中,运算符不需要成为其成员A.你可以把它变成一个自由函数,这是更好的选择(见这里的理由).

  3. 如果没有与之相关的内部排序,A并且您只需operator <将实例存储为a中的键std::map,请考虑提供命名谓词.

  • 所有这些样板都真正强调了多少恩惠`auto operator <=>(const A&)= default;`就是这样. (8认同)

Ric*_*ges 9

很高兴的回答来自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);
    }
};
Run Code Online (Sandbox Code Playgroud)

这通常会产生一种使对象和元组在比较方面可互换的一般系统的想法

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);
}
Run Code Online (Sandbox Code Playgroud)

这允许这样的代码:

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;

}
Run Code Online (Sandbox Code Playgroud)

例如:http://coliru.stacked-crooked.com/a/ead750f3f65e3ee9