【问题标题】:C++ operator overloading, understanding the Google style guideC++运算符重载,理解谷歌风格指南
【发布时间】:2010-12-06 09:07:21
【问题描述】:

我正在关注一本书来学习 C++(来自 python 背景)。我已经写了这个,它有效:

class CatalogueItem
{
    public:
        CatalogueItem();
        CatalogueItem(int item_code, const string &name, const string &description);
        ~CatalogueItem() {};

        bool operator< (const CatalogueItem &other) const;
        ...

    private:
        ...
};

...

list<CatalogueItem> my_list;

// this is just me playing around
CatalogueItem items[2];
items[0] = CatalogueItem(4, string("box"), string("it's a box"));
items[1] = CatalogueItem(3, string("cat"), string("it's a cat"));

my_list.push_back(items[0]);
my_list.push_back(items[1]);

my_list.sort();

我正在尝试的部分是使用运算符

这一切似乎都很好,但http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Operator_Overloading 似乎建议避免这样做,这正是本书所说的! (“特别是,不要重载 operator== 或 operator

我理解“创建相等和比较函子类型”是指创建比较函数,如下所示:

bool my_comparison_function(const CatalogueItem &a, const CatalogueItem &b)
{
    // my comparison code here
}

这就是风格指南所指的吗?

有没有人可以选择哪种方法更“正确”?

J

【问题讨论】:

  • 我不认为 Google 风格指南真的是最好的 C++ 指南集。如果您阅读它,他们会在几个部分中明确指出,做出决定是出于他们自己的内部原因,而不是因为他们“正确”。
  • Google 本质上对 C++ 有一种“无趣”的方法,这对于一个包含数千名才华横溢的程序员的组织来说可能是必要的,这些程序员必须以某种方式被说服以产生相互理解的 C++。但是他们的风格指南让我真的想知道他们为什么决定使用 C++,而不是 C 和一些约定来模拟虚拟调用的等价物。程序员把各种可能性摆在他们面前,然后把它们抢走,这对程序员来说似乎很残忍;-)
  • 运算符
  • “早期人们滥用运算符重载”——尤其是标准委员会,使用 auto_ptr。
  • @SteveJessop:有人对我可以参考的“Google 风格指南”进行了深入分析。在他们对 C++ 的建议中显示什么是好/坏/丑的东西。

标签: c++ syntax


【解决方案1】:

仿函数类型会更像这样:

struct CatalogueItemLessThan
{
    bool operator()(const CatalogueItem &a, const CatalogueItem &b)
    {
    }    
};

那么用法应该是这样的:

list<CatalogueItem> my_list;

// this is just me playing around
CatalogueItem items[2];
items[0] = CatalogueItem(4, string("box"), string("it's a box"));
items[1] = CatalogueItem(3, string("cat"), string("it's a cat"));

my_list.push_back(items[0]);
my_list.push_back(items[1]);

my_list.sort(CatalogueItemLessThan());

这样做的主要优点是允许您将排序与对象本身分离。您现在可以根据需要提供尽可能多的排序类型,并在不同的地方使用它们。 (例如,字符串可以按词法顺序排序,或不区分大小写,或“naturally”。

与松散函数相比,使用仿函数的优点是您可以将参数传递到比较中以修改仿函数的行为方式。

一般来说,谷歌风格指南并不是最好的风格指南(恕我直言,尤其是他们对例外的例外,但这是另一个讨论)。如果一个对象有明显的排序顺序,我通常会添加一个默认的operator&lt;。如果稍后,我想添加额外的排序顺序,那么我添加松散的功能。如果以后,我需要将参数添加到排序顺序,那么我将它们变成函子。在需要之前添加复杂性是没有意义的。

【讨论】:

  • 是的,这就是他们的意思;这些类有时被称为比较器。
  • 使用仿函数的另一个主要优点是,您可以将仿函数用作模板的类型参数,例如std::map,这是普通函数无法做到的。
  • 好,但绝对不正确! :) 您可以使用普通函数作为模板类的参数。普通函数(衰减为函数 pionters)也是 Functor,它们与 'std::map' 的比较器(例如)一起工作得很好。就试一试吧。基于类的函子的主要好处是您可以将内部状态附加到该函子,即将数据字段添加到类。普通函数不能附加这样的状态。否则,基于类的函子和普通函数没有区别。
  • @Greg Rogers:确实如此(忽略一些超级高级编译器可以做的事情)。此外,基于类的函子有时更能抵抗重载解析中的歧义。然而,这并没有改变普通函数也是 Functor 的事实,并且在允许 Functor 的任何地方都能正常工作。
  • 这不是你使用它的方式。当然,您必须将 函数指针类型 作为类型参数传递给模板。函数指针本身作为参数传递给构造函数。例如:std::map map(testLess)
【解决方案2】:

Google 想要对您说的内容如下。

如您所知,对于给定类型,您可以重载一个且只有一个运算符“other 比较标准对相同类型的对象进行排序。你打算怎么做? '

当然,您可以通过编写一个新的命名比较函数/函子(不是“

嗯,可能有。如果您有一个非常明确且明显的自然 排序方法适用于您的类型,那么将其实现为运算符'main 比较方法。其他辅助的、不太“自然”的比较方法可以而且应该作为命名函数来实现。这很好。

但是,如果您没有如此明显的“自然”比较候选者怎么办?在这种情况下,偏爱一种方法而不是另一种方法并将“

换句话说,通过重载'

【讨论】:

  • 为什么这是推荐的要点。另一方面,许多类型只需要以一种方式排序。如果您拥有所有代码,从使用 operator
【解决方案3】:

仿函数类型是 C++ 类型(类或结构),它重载 () 运算符,使该类型的实例表现得像一个函数。这类似于在 Python 中实现 __call__() 的类。

std::map 这样的一些STL 集合类型需要key_compare 函子来对内部树结构中的键进行排序,从而提供快速的访问时间。默认情况下,这是std::less,它使用operator&lt; 来比较值。因此,通常提供此运算符以允许自定义类充当 std::map(和类似的)中的键。

Google 显然不鼓励这样做,而是提供您自己的比较函子。因此,您可以执行以下操作,而不是实现 operator&lt;

struct my_compare
{
    bool operator ()(const CatalogueItem& lhs, const CatalogueItem& rhs)
    {
        ...
    }
};

如果您必须访问私有成员来实现这一点,请将 functor 声明为您的类的 friend

【讨论】:

  • 函子是一个比你上面描述的更通用的概念。 Functor 是所有接受后缀 '()' 运算符的东西。普通函数或函数指针也是函子。它不必是具有重载 '()' 的类类型的对象。
  • 然而,为了吹毛求疵,我认为 Ferdinand 对“函子类型”一词的定义是正确的。他没有说“函子是”。 +1 添加好友推荐!
猜你喜欢
  • 1970-01-01
  • 2011-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-03
  • 2014-02-14
  • 2014-07-04
相关资源
最近更新 更多