如何为我的班级提供交换功能?

时间:2011-06-17 02:51:22

标签: c++ algorithm stl swap

在STL算法中启用swap的正确方法是什么?

1)会员swapstd::swap是否使用SFINAE技巧来使用成员swap

2)在同一名称空间中自由站立swap

3)std::swap的部分专业化。

4)以上所有。

谢谢。

编辑:看起来我没有清楚地说出我的问题。基本上,我有一个模板类,我需要STL algos来使用我为该类编写的(高效)交换方法。

3 个答案:

答案 0 :(得分:89)

1)是swap的正确使用。当你写" library"代码并希望在swap上启用ADL(依赖于参数的查找)。此外,这与SFINAE无关。

// some algorithm in your code
template<class T>
void foo(T& lhs, T& rhs){
  using std::swap; // enable 'std::swap' to be found
                   // if no other 'swap' is found through ADL
  // some code ...
  swap(lhs, rhs); // unqualified call, uses ADL and finds a fitting 'swap'
                  // or falls back on 'std::swap'
  // more code ...
}

2)为您的班级提供swap功能的正确方法。

namespace Foo{

class Bar{}; // dummy

void swap(Bar& lhs, Bar& rhs){
  // ...
}

}

如果现在使用swap,如1)所示,将找到您的功能。此外,如果您绝对需要,或者提供由免费功能调用的成员swap,您可以将该功能设为朋友:

// version 1
class Bar{
public:
  friend void swap(Bar& lhs, Bar& rhs){
    // ....
  }
};

// version 2
class Bar{
public:
  void swap(Bar& other){
    // ...
  }
};

void swap(Bar& lhs, Bar& rhs){
  lhs.swap(rhs);
}

3)你的意思是明确的专业化。部分仍然是其他东西,也不可能用于函数,只有结构/类。因此,由于您无法为模板类专门设置std::swap,因此您拥有以在命名空间中提供免费功能。如果我可以这样说,那也不错。现在,也可以进行明确的专业化,但通常是you do not want to specialize a function template

namespace std
{  // only allowed to extend namespace std with specializations

template<> // specialization
void swap<Bar>(Bar& lhs, Bar& rhs){
  // ...
}

}

4)否,因为1)与2)和3)不同。此外,同时拥有2)和3)将导致总是选择2),因为它更适合。

答案 1 :(得分:1)

要回答EDIT,其中类可能是模板类,您根本不需要专门化。考虑这样一个类:

template <class T>
struct vec3
{
    T x,y,z;
};

您可以定义以下类:

vec3<float> a;
vec3<double> b;
vec3<int> c;

如果你想能够创建一个函数来实现所有3个交换(不是这个示例类保证它),你就像Xeo在(2)中说的那样......没有专业化但只是制作一个常规的模板函数:

template <class T>
void swap(vec3<T> &a, vec3<T> &b)
{
    using std::swap;
    swap(a.x,b.x);
    swap(a.y,b.y);
    swap(a.z,b.z);
}

交换模板函数应与您尝试交换的类位于同一名称空间中。即使您没有使用ADL引用该命名空间,以下方法也会查找并使用该交换:

using std::swap;
swap(a,b);

答案 2 :(得分:0)

似乎(2)(在声明了用户定义的类的同一个命名空间中,独立的swap {em} )是提供swap的唯一允许方式用户定义的类,因为将声明添加到名称空间std通常是未定义的行为。 Extending the namespace std (cppreference.com)

  

将声明或定义添加到名称空间std或嵌套在std中的任何名称空间中是不确定的行为,以下有一些例外情况

并且swap不表示为这些例外之一。因此,将自己的swap重载添加到std名称空间是一种未定义的行为。

也有人说,如果提供了用户定义的swap,则标准库使用对swap函数的非限定调用来为用户类调用用户定义的swap

Swappable (cppreference.com)

  

许多标准库函数(例如,许多算法)都希望其参数满足 Swappable 的要求,这意味着标准库每次执行交换时,都使用using std::swap; swap(t, u);的等效项。

swap (www.cplusplus.com)

  

标准库的许多组件(在std之内)以 unqualified 的方式调用swap,以允许调用非基本类型的自定义重载,而不是此泛型版本:在此通用版本上,通过依赖于参数的查找选择在与提供其类型相同的名称空间中声明的swap的自定义重载。

但是请注意,直接对用户定义的类使用std::swap函数会调用std::swap的通用版本,而不是用户定义的swap

my::object a, b;
std::swap(a, b); // calls std::swap, not my::swap

因此,建议以与标准库中相同的方式在用户代码中调用swap函数:

my::object a, b;
using std::swap;
swap(a, b); // calls my::swap if it is defined, or std::swap if it is not.