如何使set :: find()适用于自定义类对象?

时间:2012-03-20 15:16:24

标签: c++ stl set

我对使用STL set::find()一组我自己定义的类对象感到有点困惑。

我的课程包含两个以上的项目(3/4/5等),那么如何重载less运算符?

我尝试了3变量,如下所示并正常工作:

return( (a1.i < a2.i) ||
    (!(a1.i > a2.i) && (a1.f < a2.f)) ||
    (!(a1.i > a2.i) && !(a1.f > a2.f) && (a1.c < a2.c)));

其中,a1a2是类对象,(ifc是类成员。)

现在我想对n个成员进行概括,但我的find()并不总是有效。

我一直在查看STL的详细文档,试图了解set::find()的实现方式,以及为什么它需要较少的(<)运算符重载。

我提到了sgi和msdn文档,但我在那里找不到set::find()的实现细节。

我的set::find()实施中我做错了什么?

5 个答案:

答案 0 :(得分:4)

您必须定义对象的严格排序。因此,如果您的对象由n成员a_1 ... a_n组成,所有成员都有严格的排序,您可以做的是:

bool operator< (const TYPE &rhs) {
  if (a_1 < rhs.a_1) return true; else if (a_1 > rhs.a_1) return false;
  if (a_2 < rhs.a_2) return true; else if (a_2 > rhs.a_2) return false;
  ...
  if (a_n < rhs.a_n) return true;
  return false;
}

修改 如果你可以选择使用boost或C ++ 11,你应该选择Luc Danton在答案中建议的std::tie / boost::tie方法。它更干净。

答案 1 :(得分:4)

您可以使用元组轻松获得成员的词典排序:

return std::tie(lhs.i, lhs.f, lhs.c) < std::tie(rhs.i, rhs.f, rhs.c);

这要求每个成员都具有可比性,例如: lhs.i < rhs.i是有道理的。

请注意,std::tiestd::tuple仅适用于C ++ 11,因此对于C ++ 03,您可以使用例如提供boost::tieboost::tuple的Boost.Tuple使用与std::tuple相同的排序。)

至于应该去哪里,习惯上把它放在operator<中(毕竟这是使用tie以便在一开始就可以轻松订购的原因)。这个操作员经常会成为朋友,所以这看起来像是:

class foo {
public:
    /* public interface goes here */

    // declaration of non-member friend operator
    // if it doesn't need to be a friend, this declaration isn't needed
    friend
    bool operator<(foo const& lhs, foo const& rhs);

private:
    T t;
    U u;
    V v;

};

bool operator<(foo const& lhs, foo const& rhs)
{
    // could be boost::tie
    return std::tie(lhs.t, lhs.u, lhs.v) < std::tie(rhs.t, rhs.u, rhs.v);
}

正如您所看到的那样,它不是完全自动的,因为operator<的实现需要列出foo的每个成员(或者至少是那些对排序很重要的成员),两次。我害怕没有更好的方式。

您可以为operator<专门设置std::less,而不是提供foo,但这有点奇特而不是首选方式。如果成为foo的扩展接口的一部分仍然没有意义排序(例如,如果没有规范的可能有多个有序的排序),那么首选的方法是编写一个仿函数: / p>

struct foo_ordering {
    bool operator()(foo const& lhs, foo const& rhs) const
    {
        /* implementation as before, but access control/friendship
           has to be planned for just like for operator< */
    }
};

然后你会使用例如std::set<foo, foo_ordering>

请注意,无论排序采用何种形式(通过operator<std::less<foo>或仿函数),如果它与std::set或任何其他关联容器一起使用(以及默认情况下,例如std::set<T>使用std::less<T>,默认情况下使用operator<)它必须遵循一些严格的标准,即它必须是严格的弱排序。但是,如果用于foo排序的所有成员都有SW顺序,则生成的词典排序也是SW顺序。

答案 2 :(得分:2)

std :: set元素比较函数应该在元素域上定义Strict Weak Ordering关系。使用这个定义我们可以说如果compare(a,b)为false且compare(b,a)也为false,则两个元素是等价的。 std :: find可以使用这个假设来实现 您可以在此处找到更多信息:http://www.sgi.com/tech/stl/set.htmlhttp://www.sgi.com/tech/stl/StrictWeakOrdering.html

答案 3 :(得分:1)

您的operator <应该能够将每个对象与给定的对象进行比较,就像那样

struct Data
{
    bool operator < (const Data& right) const
    {
    return( (this.i < right.i) ||
        (!(this.i > right.i) && (this.f < right.f)) ||
        (!(this.i > right.i) && !(this.f > right.f) && (this.c < right.c)));
    }
}

此外,您的比较算法看起来很可疑,因为它不会考虑案例,

this.i == right.i

this.f == right.f

您实际上不应该对std::set实施感兴趣。它可以从编译器更改为编译器,并且可以在将来进行修改。您的程序应仅对容器接口进行假设,而不是实现

答案 4 :(得分:1)

这只是部分答案,但可以在the website of SGI找到详细的STL文档。