代码之家  ›  专栏  ›  技术社区  ›  Peter Coulton

为结构定义运算符<

  •  52
  • Peter Coulton  · 技术社区  · 7 年前

    我有时用小的 structs 作为地图上的键,所以我必须定义 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;
        }
    };
    

    操作员< 为了一个 struct 或 class ?

    我知道有些人喜欢用 memcmp(this, &rhs, sizeof(MyStruct)) < 0 ,但如果成员之间存在填充字节,或者 char 可能在空终止符之后包含垃圾的字符串数组。

    14 回复  |  直到 13 年前
        1
  •  108
  •   Konrad Rudolph    13 年前

    这是一个很老的问题,因此这里所有的答案都过时了。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);
    }
    

    为什么这比使用 boost::make_tuple ? 因为 make_tuple 将创建所有数据成员的副本,这可能很昂贵。 std::tie 相反,只会创建一个引用的薄包装(编译器可能会完全优化它)。

        2
  •  19
  •   Mike Seymour    13 年前

    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);
    }
    

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

    std::tie()

        3
  •  9
  •   Benoit    16 年前

    我会这样做:

    #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
    
        4
  •  6
  •   Steve Townsend    16 年前

    boost::tuple<int, int, int> -它的 operator<

        5
  •  4
  •   justintime    10 年前

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

    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;
        }
    };
    
        6
  •  3
  •   Community Mohan Dere    9 年前

    我知道最好的方法是用 boost tuple

    #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 )
       ...
    

    suggestion to use temporary tuples through boost's make_tuple

        7
  •  3
  •   Frigo    15 年前

    我通常是这样实现词典排序的:

    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
        }
        ...
    }
    

    请注意,浮点值(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;
        }
        ...
    }
    
        8
  •  2
  •   usta    16 年前
    #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;
    }
    
        9
  •  2
  •   Nim    16 年前

    如果你不能使用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;
    }
    

    编辑:

    根据评论修改,这个版本现在也应该短路了,现在使用两个bool来保持状态(不确定是否有办法用一个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;
    };
    

    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);
    }
    

    只需要建立一个这样的函子集合来进行各种比较。。

        10
  •  1
  •   mskfisher KeithS    16 年前

    我刚学会 boost::tuple

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

    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);
    }
    

    我喜欢它,因为它把所有的东西都设置成平行的结构,使错误和遗漏更容易被发现。

    但是,当然,无论如何,你都在进行单元测试,对吧?

        11
  •  0
  •   Mark B    16 年前

    class A
    {
    int a;
    int b;
    int c;
    

    它会发出:

    bool operator<(const A& left, const A& right)
    {
        bool result(false);
    
        if(left.a != right.a)
        {
            result = left.a < right.a;
        }
        else if(left.b != right.b)
        {
            result = left.b < right.b;
        }
        else
        {
            result = left.c < right.c;
        }
    
        return result;
    }
    

    #!/usr/bin/perl
    
    use strict;
    
    main:
    
    my $line = <>;
    chomp $line;
    $line =~ s/^ *//;
    
    my ($temp, $line, $temp) = split / /, $line;
    
    print "bool operator<(const $line& left, const $line& right)\n{\n";
    print "    bool result(false);\n\n";
    
    my $ifText = "if";
    
    $line = <>;
    
    while($line)
    {
        if($line =~ /{/)
        {
            $line = <>;
            next;
        }
        if($line =~ /}/)
        {
            last;
        }
    
        chomp $line;
        $line =~ s/^ *//;
    
        my ($type, $name) = split / /, $line;
        $name =~ s/; *$//;
    
        $line = <>;
        if($line && !($line =~ /}/))
        {
            print "    $ifText(left.$name != right.$name)\n";
            print "    {\n";
            print "        result = left.$name < right.$name;\n";
            print "    }\n";
    
            $ifText = "else if";
        }
        else
        {
            print "    else\n";
            print "    {\n";
            print "        result = left.$name < right.$name;\n";
            print "    }\n";
    
            last;
        }
    }
    
    print "\n    return result;\n}\n";
    
        12
  •  0
  •   nothrow    16 年前
    bool operator <(const A& l, const A& r)
    {
    
        int[] offsets = { offsetof(A, a), offsetof(A, b), offsetof(A, c) };
        for(int i = 0; i < sizeof(offsets)/sizeof(int); i++)
        {
            int ta = *(int*)(((const char*)&l)+offsets[i]);
            int tb = *(int*)(((const char*)&r)+offsets[i]);
    
            if (ta < tb)
                 return true;
            else if (ta > tb)
                 break;
    
        }
        return false;
    }
    
        13
  •  0
  •   Cheers and hth. - Alf    16 年前

    当您可以在定义字典顺序的元素上生成迭代器时 std::lexicographic_compare ,来自 <algorithm> .

    #include <iostream>
    
    int compared( int a, int b )
    {
        return (a < b? -1 : a == b? 0 : +1);
    }
    
    struct MyStruct
    {
        friend int compared( MyStruct const&, MyStruct const& );
        int a;
        int b;
        int c;
    
        bool operator<( MyStruct const& rhs ) const
        {
            return (compared( *this, rhs ) < 0);
        }
    };
    
    int compared( MyStruct const& lhs, MyStruct const& rhs )
    {
        if( int x = compared( lhs.a, rhs.a ) ) { return x; }
        if( int x = compared( lhs.b, rhs.b ) ) { return x; }
        if( int x = compared( lhs.c, rhs.c ) ) { return x; }
        return 0;
    }
    
    int main()
    {
        MyStruct const  s1 = { 0, 4, 8 };
        MyStruct const  s2 = { 0, 4, 9 };
        std::cout << ( s1 < s2 ? "is less" : "is not less" ) << std::endl;
    }
    

    我包括最后一个 if 和 return 在 compare return compared( lhs.c, rhs.c ) 在那里(也许你更喜欢这样)。

    干杯。,

    阿尔夫

        14
  •  0
  •   supercat    16 年前

      return compare1(a.f1,b.f1, compare2(a.f2,b.f2, compare3(a.f3,b.f3,false)));