比较函数类型与运算符<

时间:2008-10-08 16:17:28

标签: c++ stl coding-style

Google C++ Style Guide中,section on Operator Overloading建议不要重载任何运算符(“罕见的特殊情况除外”)。具体来说,它建议:

  

特别是,不要超负荷   operator==operator<就这样   你的班级可以作为一个关键   STL容器;相反,你应该   创建平等和比较仿函数   声明容器时的类型。

我对这样的仿函数看起来有点模糊,但我的主要问题是,为什么你想为此编写自己的仿函数?不会定义operator<,并且使用标准std::less<T>函数会更简单吗?使用一个优于另一个是否有任何优势?

7 个答案:

答案 0 :(得分:16)

除了更基本的类型之外,小于操作并不总是微不足道,甚至平等可能因情况而异。

想象一下航空公司想要为所有乘客分配登机号码的情况。这个数字反映了登机顺序(当然)。现在,是谁决定谁来谁?您可以采取客户注册的顺序 - 在这种情况下,小于操作将比较登记时间。您可能还会考虑客户为其机票支付的价格 - 低于现在比较机票价格的价格。

......等等。总而言之,在operator <课程中定义Passenger是没有意义的,尽管可能需要将乘客安排在已排序的容器中。我认为这就是谷歌所警告的。

答案 1 :(得分:6)

通常,定义operator<会更好更简单。

您需要使用仿函数的情况是需要多种方法来比较特定类型。例如:

class Person;

struct CompareByHeight {
    bool operator()(const Person &a, const Person &b);
};

struct CompareByWeight {
    bool operator()(const Person &a, const Person &b);
};

在这种情况下,可能没有一种好的“默认”方式来比较和排序人,因此不定义operator<并使用仿函数可能会更好。您也可以说通常人们按高度排序,因此operator<只调用CompareByHeight,任何需要按权重排序的人都必须明确使用CompareByWeight

通常,问题在于定义函子是由类的用户决定的,因此每当需要在有序容器中使用类时,您往往会对同一事物进行许多重新定义。

答案 2 :(得分:5)

嗯,根据您引用的网页,对于仿函数来说没有多少优势(“[运营商]可以欺骗我们的直觉,认为昂贵的操作是廉价的内置操作。”)

我的感觉是,你应该努力让你尽可能地让你成为一流的物品,对我来说,这意味着要让他们理解尽可能多的操作员。

我写了一个仿函数已经有一段时间了,但它看起来像这样:

class MyClass {....}

class LessThanMyClass : std:binary_function<MyClass, MyClass, bool>
{
    public bool operator()(MyClass lhs, MyClass rhs) 
    {   return /* determine if lhs < rhs */ ; }
}

vector<MyClass> objs;
std::sort(objs.begin(), objs.end(), LessThanMyClass());

}

答案 3 :(得分:3)

我可能不会像谷歌风格指南那样。

我认为他们得到的是当你重载operator<operator==时,你正在决定每种类型的使用,而算子类型只适用于那个集合。

如果您需要比较器的唯一方法是将项目放入集合中,那么最好具有专门针对该上下文的函数,而不是适用于所有上下文的运算符。

您可能希望按时间顺序对采购订单进行排序,但一般来说,将它们与总价格进行比较是有意义的。如果我们重载operator<来比较日期以便我们可以将它们加载到集合中,那么我们就会引入另一个客户可能滥用我们认为比较总价格的operator<的风险。

答案 4 :(得分:3)

我认为背后的信息没有定义运算符&lt;是排序是集合的属性,而不是对象的属性。相同对象的不同集合可以具有不同的排序。因此,在指定集合的​​类型而不是运算符&lt;。

时,应使用单独的函子

在实践中,很多类可能都有自然顺序,这是应用程序中集合中使用的唯一顺序。在其他情况下,订购甚至可能与应用程序无关,只与集合相关,因此它可以在以后查找项目。在这些情况下,定义运算符&lt;。

非常有意义

请记住,当我们设计对象模型时,我们只是为现实世界的一个子集建模。在现实世界中,可能有多种不同的方法来对同一类的对象进行排名,但在我们工作的应用程序域中,可能存在一个相关的对象。

如果代码演变为需要与第一个相关的第二个排序,则应该重构该类以删除运算符&lt;并将两个排名函数放在单独的仿函数中。这表明没有一个排名比其他排名更重要的意图。

关于算术运算符,除非要实现算术类型,否则不应重载它们。

当然,每条规则都有例外。如果你不知道你是否应该例外,你可能不应该。经验将成为您的指导。

答案 5 :(得分:2)

仿函数是一个operator ()的类。在这种情况下,该方法将采用被比较类型的两个参数,如果第一个小于第二个,则返回bool结果。

编辑:要建立James Curran所说的内容,您可以在课程中定义您的仿函数。所以,例如:

class MyClass
{
    struct LessThan : public std::binary_function<MyClass, MyClass, bool>
    {
        bool operator()(const MyClass & first, const MyClass & second) const
        {
            return first.key < second.key;
        }
    };
};

答案 6 :(得分:1)

具有讽刺意味的是,仿函数还需要覆盖一个运算符(函数调用运算符 - operator ()),所以我不确定它们的意义。