你应该在std命名空间中重载swap吗?

Dav*_*vid 20 c++ stl

我今天读到一些有趣的内容,说用户提供的类型(作为模板参数提供)调用swap的"标准"方式是......

using std::swap;
swap(something, soemthingelse);
Run Code Online (Sandbox Code Playgroud)

这样做的原因是使用的参数依赖查找要么使用一个swap在用户的命名空间或函数swapstd命名空间.这为我提出了一个感兴趣的问题.当我超载std::swap了我的课之一,我实际上已被定义它的std命名空间... namespace std { void swap(/*...*/){/*...*/} }.这种做法错了吗?我应该定义自己的swaps std或我自己的命名空间(以及为什么)?

Jon*_*ely 20

你这样做是错的 :)

17.6.2.4.1 [namespace.std]

  1. 如果C++程序将声明或定义添加到命名空间std或命名空间中的命名空间std,则除非另有说明,否则C++程序的行为是未定义的.std只有当声明取决于用户定义的类型并且特化符合原始模板的标准库要求且未明确禁止时,程序才可以将任何标准库模板的模板特化添加到命名空间.

很清楚地说,您可能不会向命名空间添加重载std.你可以专门std::swap<MyType>为你的类型,但如果你的类型是一个模板,你需要一个部分专业化,std::swap<MyContainer<T>>你不能部分专门化一个功能模板,所以这将无法工作,所以它一般不是一个好方法.

C++ 11还定义了可交换类型的要求,包括:

17.6.3.2 [swappable.requirements]

  1. ...
  2. ...
  3. 其中swap(t, u)swap(u, t)被评估的上下文应确保在候选集上通过重载解析(13.3)选择名为"swap"的二进制非成员函数,该候选集包括:
  • <utility>(20.2)和中定义的两个交换函数模板
  • 由参数依赖查找(3.4.2)生成的查找集.

因此,调用swap两个可交换类型的对象应该能够找到std::swap并且应该能够通过ADL找到其他重载.调用它不合格(并且没有显式模板参数列表)可确保ADL发生,并包含<utility>和添加using声明以std::swap确保可以找到标准重载.因此,您在问题中显示的方式符合这些要求.

这非常明确地定义了标准所使用的可交换性,这是标准库所需要的,例如通过函数<algorithm>.

如果您swap在类型的命名空间中为您的类型添加了重载,那么ADL就可以找到它们.无论如何,这是正确的事情,与您的类型相关的函数属于与您的类型相同的命名空间,有关该主题的更多详细信息,请参阅Sutter和Alexandrescu 在C++编码标准中的第57项.

简而言之,你做错了.你读的是正确的.执行using std::swap和依赖ADL始终有效(对于模板和非模板)并避免未定义的行为.好极了.

注意:关于如何交换用户定义的类型,C++ 03标准还不太清楚.有关该区域的一些历史,请参阅N1691 2.2,它定义了术语定制点,并显示了在API中定义它们的不同方法.C++ 11中用于交换类型的协议使用其中一种方式,现在明确无误地祝福为您的类型提供交换功能的"正确方法".其他库中的其他自定义点可以使用其他方法,但是可以在C++中交换11术语意味着using std::swap;并依赖于ADL.


Dav*_*eas 6

为您自己的类型提供标准模板的特化是合法的,这些模板必须在std命名空间内.所以这两种方法都是合法的C++.

话虽这么说,推荐的方法是swap在与您自己的类型相同的命名空间中提供自由函数,并让ADL从那里获取重载.


编辑:在一些评论之后,我重读了这个问题并注意到它提到了命名空间中的重载std.它是非法提供在重载std命名空间中,只有专业化是允许的.