C++ 为结构定义 operator<

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3882467/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 13:57:32  来源:igfitidea点击:

Defining operator< for a struct

c++operator-overloading

提问by Kristopher Johnson

I sometimes use small structsas keys in maps, and so I have to define an operator<for them. Usually, this ends up looking something like this:

我有时structs在地图中使用 small作为键,所以我必须operator<为它们定义一个。通常,这最终看起来像这样:

struct MyStruct
{
    A a;
    B b;
    C c;

    bool operator<(const MyStruct& rhs) const
    {
        if (a < rhs.a)
        {
           return true;
        }
        else if (a == rhs.a)
        {
            if (b < rhs.b)
            {
                return true;
            }
            else if (b == rhs.b)
            {
                return c < rhs.c;
            }
        }

        return false;
    }
};

This seems awfully verbose and error-prone. Is there a better way, or some easy way to automate definition of operator<for a structor class?

这看起来非常冗长且容易出错。有没有更好的方法,或者一些简单的方法来自动定义operator<for astructclass

I know some people like to just use something like memcmp(this, &rhs, sizeof(MyStruct)) < 0, but this may not work correctly if there are padding bytes between the members, or if there are charstring arrays that may contain garbage after the null terminators.

我知道有些人喜欢只使用类似的东西memcmp(this, &rhs, sizeof(MyStruct)) < 0,但是如果成员之间有填充字节,或者如果char在空终止符之后有可能包含垃圾的字符串数组,这可能无法正常工作。

回答by Konrad Rudolph

This is quite an old question and as a consequence all answers here are obsolete. C++11 allows a more elegant and efficient solution:

这是一个相当古老的问题,因此这里的所有答案都已过时。C++11 提供了更优雅、更高效的解决方案:

bool operator <(const MyStruct& x, const MyStruct& y) {
    return std::tie(x.a, x.b, x.c) < std::tie(y.a, y.b, y.c);
}

Why is this better than using boost::make_tuple? Because make_tuplewill create copies of all the data members, which can be costly. std::tie, by contrast, will just create a thin wrapper of references (which the compiler will probably optimise away entirely).

为什么这比使用更好boost::make_tuple?因为make_tuple会创建所有数据成员的副本,这可能代价高昂。std::tie,相比之下,只会创建一个薄的引用包装器(编译器可能会完全优化掉)。

In fact, the above code should now be considered the idiomatic solution to implementing a lexicographical compare for structures with several data members.

事实上,上面的代码现在应该被认为是为具有多个数据成员的结构实现字典序比较的惯用解决方案。

回答by Mike Seymour

Others have mentioned boost::tuple, which gives you a lexicographical comparison. If you want to keep it as a structure with named elements, you can create temporary tuples for comparison:

其他人提到过boost::tuple,这给你一个字典序的比较。如果要将其保留为具有命名元素的结构,则可以创建临时元组进行比较:

bool operator<(const MyStruct& x, const MyStruct& y)
{
    return boost::make_tuple(x.a,x.b,x.c) < boost::make_tuple(y.a,y.b,y.c);
}

In C++0x, this becomes std::make_tuple().

在 C++0x 中,这变成了std::make_tuple().

UPDATE: And now C++11 is here, it becomes std::tie(), to make a tuple of references without copying the objects. See Konrad Rudolph's new answer for details.

更新:现在 C++11 来了,它变成std::tie()了在不复制对象的情况下创建引用元组。有关详细信息,请参阅康拉德·鲁道夫 (Konrad Rudolph) 的新答案。

回答by Benoit

I would do this:

我会这样做:

#define COMPARE(x) if((x) < (rhs.x)) return true; \
                   if((x) > (rhs.x)) return false;
COMPARE(a)
COMPARE(b)
COMPARE(c)
return false;
#undef COMPARE

回答by Steve Townsend

In this case you can use boost::tuple<int, int, int>- its operator<works just the way you want.

在这种情况下,您可以使用boost::tuple<int, int, int>- 它的operator<按照您想要的方式工作。

回答by bHymanfly

I think the easiest way is to stick with the < operator for all comparisons and don't use > or ==. Below is the pattern I follow, and you can follow for all your structs

我认为最简单的方法是在所有比较中都使用 < 运算符,不要使用 > 或 ==。以下是我遵循的模式,您可以遵循所有结构

typedef struct X
{
    int a;
    std::string b;
    int c;
    std::string d;

    bool operator <( const X& rhs ) const
    {
        if (a < rhs.a) { return true; }
        else if ( rhs.a < a ) { return false; }

        // if neither of the above were true then 
        // we are consdidered equal using strict weak ordering
        // so we move on to compare the next item in the struct

        if (b < rhs.b) { return true; }
        if ( rhs.b < b ) { return false; }

        if (c < rhs.c) { return true; }
        if ( rhs.c < c ) { return false; }

        if (d < rhs.d) { return true; }
        if ( rhs.d < d ) { return false; }

        // if both are completely equal (based on strict weak ordering)
        // then just return false since equality doesn't yield less than
        return false;
    }
};

回答by Frigo

I usually implement lexicographical ordering this way:

我通常以这种方式实现字典顺序:

bool operator < (const MyObject& obj)
{
    if( first != obj.first ){
        return first < obj.first;
    }
    if( second != obj.second ){
        return second < obj.second;
    }
    if( third != obj.third ){
        return third < obj.third
    }
    ...
}

Mind you it needs extra consideration for floating point values (G++ warnings), for those something like this would be better:

请注意,它需要额外考虑浮点值(G++ 警告),对于这样的事情会更好:

bool operator < (const MyObject& obj)
{
    if( first < obj.first ){
        return true;
    }
    if( first > obj.first ){
        return false;
    }
    if( second < obj.second ){
        return true;
    }
    if( second > obj.second ){
        return false;
    }
    ...
}

回答by Peter G.

The best way I know is to use a boost tuple. It offers among others a builtin comparison and constructors.

我所知道的最好方法是使用boost 元组。它提供了一个内置的比较和构造函数。

#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>

typedef boost::tuple<int,int,int> MyStruct;

MyStruct x0(1,2,3), x1(1,2,2);
if( x0 < x1 )
   ...

I also like Mike Seymors suggestion to use temporary tuples through boost's make_tuple

我也喜欢 Mike Seymors 的建议,即通过 boost 的 make_tuple 使用临时元组

回答by usta

#include <iostream>

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/less.hpp>

struct MyStruct {
   int a, b, c;
};

BOOST_FUSION_ADAPT_STRUCT( MyStruct,
                           ( int, a )
                           ( int, b )
                           ( int, c )
                          )

bool operator<( const MyStruct &s1, const MyStruct &s2 )
{
   return boost::fusion::less( s1, s2 );
}

int main()
{
   MyStruct s1 = { 0, 4, 8 }, s2 = { 0, 4, 9 };
   std::cout << ( s1 < s2 ? "is less" : "is not less" ) << std::endl;
}

回答by Nim

if you can't use boost, you could try something like:

如果您不能使用 boost,您可以尝试以下操作:

#include <iostream>

using namespace std;

template <typename T>
struct is_gt
{
  is_gt(const T& l, const T&r) : _s(l > r) {}

  template <typename T2>
  inline is_gt<T>& operator()(const T2& l, const T2& r)
  {
    if (!_s)
    {
      _s = l > r;
    }
    return *this;
  }

  inline bool operator!() const { return !_s; }

  bool _s;
};

struct foo
{
  int a;
  int b;
  int c;

  friend bool operator<(const foo& l, const foo& r);
};

bool operator<(const foo& l, const foo& r)
{
  return !is_gt<int>(l.a, r.a)(l.b, r.b)(l.c, r.c);
}

int main(void)
{
  foo s1 = { 1, 4, 8 }, s2 = { 2, 4, 9 };
  cout << "s1 < s2: " << (s1 < s2) << endl;
  return 0;
}

I guess this avoids any macros, and as long as the types in the structure support <, it should work. Of course there is overhead for this approach, constructing is_gt and then superflous branches for each parameter if one of the values is greater...

我想这避免了任何宏,只要结构中的类型支持 <,它就应该可以工作。当然,这种方法存在开销,如果其中一个值更大,则为每个参数构造 is_gt 和多余的分支......

Edit:

编辑:

Modified based on comments, this version should now short-circuit as well, now uses two bools to keep state (not sure there's a way to do this with a single bool).

根据评论修改,这个版本现在也应该短路,现在使用两个布尔值来保持状态(不确定有没有办法用一个布尔值来做到这一点)。

template <typename T>
struct is_lt
{
  is_lt(const T& l, const T&r) : _s(l < r), _e(l == r) {}

  template <typename T2>
  inline bool operator()(const T2& l, const T2& r)
  {
    if (!_s && _e)
    {
      _s = l < r;
      _e = l == r;
    }
    return _s;
  }

  inline operator bool() const { return _s; }

  bool _s;
  bool _e;
};

and

bool operator<(const foo& l, const foo& r)
{
  is_lt<int> test(l.a, r.a);
  return test || test(l.b, r.b) || test(l.c, r.c);
}

just build up a collection of such functors for various comparisons..

只需建立一个此类函子的集合即可进行各种比较。

回答by mskfisher

I just learned the boost::tupletrick, thanks, @Mike Seymour!

我刚刚学会了boost::tuple诀窍,谢谢@Mike Seymour!

If you can't afford Boost, my favorite idiom is:

如果你买不起 Boost,我最喜欢的成语是:

bool operator<(const MyStruct& rhs) const
{
    if (a < rhs.a)  return true;
    if (a > rhs.a)  return false;

    if (b < rhs.b)  return true;
    if (b > rhs.b)  return false;

    return (c < rhs.c);
}

which I like because it sets everything in parallel structure that makes errors and omissions easier to spot.

我喜欢它,因为它以并行结构设置所有内容,使错误和遗漏更容易发现。

But, of course, you are unit testing this anyway, right?

但是,当然,无论如何,您正在对此进行单元测试,对吗?