启用我swap
的STL算法的正确方法是什么?
1)会员swap
。是否std::swap
使用SFINAE技巧来使用成员swap
。
2)swap
在同一个命名空间中独立。
3)的部分专业化std::swap
。
4)以上全部。
谢谢。
编辑:好像我没有清楚地说出我的问题。基本上,我有一个模板类,我需要STL算法才能使用为该类编写的(有效)交换方法。
Answers:
swap
。当您编写“库”代码并要在上启用ADL(依赖于参数的查找)时,请以这种方式编写swap
。另外,这与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 ...
}
swap
为您的类提供功能的正确方法。namespace Foo {
class Bar{}; // dummy
void swap(Bar& lhs, Bar& rhs) {
// ...
}
}
如果swap
现在按1)所示使用,将找到您的功能。另外,如果您确实需要,可以将该函数设为朋友,或者提供一个swap
由free函数调用的成员:
// 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);
}
...
std::swap
研究模板类,因此必须在名称空间中提供免费功能。如果我可以这么说,这不是一件坏事。现在,也可以进行显式专门化,但是通常您不想专门对功能模板进行专门化:namespace std
{ // only allowed to extend namespace std with specializations
template<> // specialization
void swap<Bar>(Bar& lhs, Bar& rhs) noexcept {
// ...
}
}
using std::swap;
没有启用ADL,它只是允许编译器定位std::swap
ADL是否找不到适当的重载。
要回答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)(在声明用户定义的类的同一名称空间中自由站立swap
)是提供swap
用户定义的类的唯一允许方式,因为向声明名称空间添加声明std
通常是未定义的行为。扩展命名空间std(cppreference.com):
将声明或定义添加到名称空间
std
或嵌套在std
其中的任何名称空间是未定义的行为,以下有一些例外
并且swap
不表示为这些例外之一。因此,将您自己的swap
重载添加到std
名称空间是未定义的行为。
也有人说,如果提供了用户swap
定义的标准库,则标准库使用对函数的非限定调用来swap
为用户类调用用户定义的swap
。
许多标准库函数(例如,许多算法)都希望其参数满足Swappable的要求,这意味着,每当标准库执行交换时,它都使用的等效项
using std::swap; swap(t, u);
。
标准库中的许多组件(以内
std
)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.