如何实现swap()?

时间:2012-07-24 16:22:12

标签: c++ swap

  

可能重复:
  how to provide a swap function for my class?

每次我认为我理解它,我都会看到让我感到困惑的事情。

如果您想为自己的班级提供swap的实施,您会怎么做?

可能性列表如下:

  1. Define one inside the std namespace (taking two arguments),在下面标注#3 (有人说这是正确的;有些人说非法)

  2. 在类中定义一个静态方法,使用两个参数来交换
    (对我来说比#4更有意义,但我不明白为什么没有人这样做),根据需要调用任何基类swap

  3. Define an instance method inside the class, taking one other argument to swap with,根据需要调用任何基类swap

  4. Define an instance method inside the class, taking two other arguments to swap,也是here,根据需要调用任何基类swap

  5. Define one in your own namespace (taking two arguments),称为#3

  6. 其他

  7. 我自己的理解是我需要#5和#3,然后调用者会像swap那样调用using std::swap; swap(a, b);,但事实上似乎没有人表明这种组合让我很困惑。我真的根本不理解#4,因为实际上每个人似乎都在使用实例成员,而实际操作是静态的。我无法分辨我的理解是错误的还是我在查看时看到的一堆答案。

    正确的方法是什么?

3 个答案:

答案 0 :(得分:7)

根据您自己的理解,我见过的常见模式是提供3和5。

  1. std::命名空间添加了一个特殊化,这是允许的,但在所有情况下都可能无法实现(如果您的类型本身就是模板)。
  2. 完全没有任何优势,并且当在其中一个成员之外使用时强制使用该类型,这意味着对于将您的类型作为成员的其他类型实施swap,他们将需要符合条件电话会议(void swap( other& l, other& r ) { T::swap( l.t, r.t ); }
  3. 不需要友谊,允许与rvalue一起使用(即使在C ++ 03中),在某些情况下std::vector<int>().swap( v );是惯用的,以清除向量的内容。
  4. 什么?你误解了代码!这并不是声明一个成员接受两个参数,而是一个带有两个参数的自由函数,并定义内联函数。这相当于5(不转发为3,而是实现自由函数中的所有内容)。
  5. 同一命名空间中的自由函数允许ADL查找它,并允许其他代码使用void swap( other& l, other& r ) { using std::swap; swap( l.t, r.t ); }的公共模式,而无需知道other::t的类型是否具有特定{{1}需要使用swap中的重载或过载。转发为3允许您提供可以通过ADL以及临时对象使用的单个(实际)实现。

答案 1 :(得分:4)

正如您所说,您需要#5(与您的类型在同一名称空间中的函数)来支持惯用语using std::swap; swap(a,b);。这样,您的重载将通过依赖于参数的查找来选择,优先于std::swap

如果swap的实现需要访问类型的私有,那么您将需要调用#3之类的成员函数,或者将非成员函数声明为friend。这就是#4中的示例:可以在类中声明和定义friend函数,但使其成为成员;它仍然在周围的命名空间中。

所以这个:

class thing {
    friend void swap(thing & a, thing & b) {/*whatever*/}
};

相当于此(或多或少 - 见评论):

class thing {
    friend void swap(thing & a, thing & b);
};

inline void swap(thing & a, thing & b) {/*whatever*/}
允许使用

#1(适用于您的类型的std::swap),但有些人会认为将所有内容保存在您自己的命名空间中更清晰。

#2和#3都不允许不合格的swap(a,b)找到您的实施。

答案 2 :(得分:4)

C ++ 11标准表示,如果tu是有效的表达式,则对象swap(t, u)可以与对象swap(u, t)进行交换从包含swap中的两个std::swap模板的重载集中选择名为<utility>的非成员函数,以及由参数依赖查找找到的任何重载,以便t的值和u被交换。

在上述条件下交换两个物体的传统方法是:

using std::swap;
swap(t, u);

此处名称查找将考虑std::swap以及ADL找到的swap的任何重载,然后重载决策将选择最佳匹配。

考虑每个实施:

  1. 向命名空间std添加重载是不合法的,并且上面的规则无论如何都不需要它们。如果专门化是针对用户定义的类型(即不是标准库类型或基本类型),那么专门化标准std::swap函数模板是合法的。如果您专门化std::swap它是有效的,否则不是,并且你不能部分专门化功能模板。

  2. swap(t, u)无法找到静态成员函数,因为它需要合格,例如Foo::swap(t, u)

  3. 一元swap成员函数无法调用为swap(t, u),必须将其称为t.swap(u)

  4. 您提供的链接显示了一个非成员 friend函数,可以通过ADL找到,因此可用于使类型可交换。

    < / LI>
  5. ADL可以找到这样的功能。

  6. 所以2和3不能使类型可交换。 4确实,5确实。 1如果正确完成,可能会做,但不能用于交换类模板。

    3不是必需的,但可用于帮助实现4或5,因为成员函数可以访问类型的内部详细信息,因此可以交换私有成员。对于4,该功能是朋友,所以已经访问。因此,通过消除过程,您需要 4 3和5.通常认为提供成员交换(3)然后提供非成员函数会更好在相同的名称空间(5)中调用成员交换。