C++ 为什么泛型编程设计更喜欢自由函数而不是成员函数?

C++ 为什么泛型编程设计更喜欢自由函数而不是成员函数?,c++,generics,boost,C++,Generics,Boost,我最近被介绍了一些通用编程库的设计,比如STL、boost::graph、boost PropertyMaps 使用自由函数如get(PropertyMap,key)而不是成员函数如PropertyMap.get(key)的基本原理是什么 我知道这些函数的最通用形式是在“boost”名称空间中定义的。假设我在我的名称空间“project”中定义了一个新的PropertyMap,那么定义它对应的“get”函数的最佳位置是什么?“boost”或“project”自由函数与特定实现的耦合不太紧密(因为

我最近被介绍了一些通用编程库的设计,比如STL、boost::graph、boost PropertyMaps

使用自由函数如get(PropertyMap,key)而不是成员函数如PropertyMap.get(key)的基本原理是什么


我知道这些函数的最通用形式是在“boost”名称空间中定义的。假设我在我的名称空间“project”中定义了一个新的PropertyMap,那么定义它对应的“get”函数的最佳位置是什么?“boost”或“project”自由函数与特定实现的耦合不太紧密(因为它们只依赖于容器的公共接口)。这允许更大的灵活性和易于维护

您将注意到,当一个特定的实现很重要时(例如
std::map::find
),标准库使用成员函数


Scott Meyers有一套规则可以很好地决定如何创建函数:

主要的动机是偏爱非成员非朋友函数有助于保持类尽可能简洁。请参阅Herb Sutter的文章:

本文还包含问题的另一部分的答案,即在哪里放置相应的get函数。它是C++的一个特性,称为依赖于参数的查找(ADL)。来自Herb Sutter,他称之为Koenig lookup,尽管这个名字有争议(见下面的评论):

Koenig lookup说,如果您提供一个类的函数参数 键入,然后查找函数名 编译器需要查看,而不仅仅是在 本地范围,但也在包含 参数的类型

下面是一个例子:

namespace MyNamespace {
    class MyClass {... };
    void func(MyClass);
}

int main(int aArgc, char* aArgv[]) {
    MyNamespace::MyClass inst;
    func(inst);  // Ok, because Koenig says look in the argument's namespace for func
}
简而言之,您只需在与类相同的命名空间中声明get函数


请注意,如果必须显式地提供模板参数,那么这对模板化函数不起作用——请参阅本文:

我认为线索在问题中——它使它们更通用。像
std::find()
这样的自由函数可以用于大量不同的容器,因此是通用的-像
std::map.find()这样的成员只适用于
std::map
,因此根本不是通用的


有了好的泛型函数,您应该能够设计自己的容器来与它们进行接口,从而不再需要在容器中编写自己的方法(当然也不需要编写自己的自由函数)。当您有一个容器时,提供一种方法是有意义的,对于该容器,特定操作可以以特定于该容器的方式比在通用函数中更好地实现(
std::map
find()
方法就是一个明显的例子)。

有两个不同的原因:

  • 更好的封装:通过最小化可以访问类属性的函数数量,可以改进封装

  • <> >强>扩展性< /强>:在C++中,命名空间定义是打开的(可以添加),而类定义是关闭的;因此,可以添加自由函数,但不能添加成员函数


虽然封装更多的是一个品味问题,但可扩展性在泛型编程中极其重要。您不想因为第三方类型缺少您需要的一种方法而不得不放弃它。。。当然,有些类型(如内置类型或标准库类型)根本无法扩展。

大部分可能是ADL。另请看,有人告诉我,当人们称之为“Koenig查找”时,Andy Koenig非常不高兴,尽管我从未亲自问过他。正确的术语是ADL或参数相关查找。除非我弄错了,安迪只为运营商提出ADL。其余的是C++标准化委员会的发明…而且会带来无尽的麻烦。”…一个特定的操作可以通过特定于该容器的方式更好地实现…--这听起来像是[部分]模板专门化的工作。