许多标准容器(如std::list和std::vector)在排序甚至赋值过程中都会使用std::swap()。
但是swap()的标准实现是非常通用的,并且对于自定义类型来说效率相当低。
因此,可以通过使用特定于自定义类型的实现重载std::swap()来获得效率。但是,如何实现它才能被std容器使用呢?
发布于 2010-04-22 00:02:22
重载std::swap的实现(也称为专门化它)的正确方法是将它写在与您要交换的东西相同的名称空间中,这样就可以通过argument-dependent lookup (ADL)找到它。
class X
{
// ...
friend void swap(X& a, X& b)
{
using std::swap; // bring in swap for built-in types
swap(a.base1, b.base1);
swap(a.base2, b.base2);
// ...
swap(a.member1, b.member1);
swap(a.member2, b.member2);
// ...
}
};发布于 2011-12-09 07:52:13
注意: Mozza314
这里模拟了一个通用std::algorithm调用std::swap的效果,并让用户在名称空间std中提供他们的交换。由于这是一个实验,因此此模拟使用namespace exp而不是namespace std。
// simulate <algorithm>
#include <cstdio>
namespace exp
{
template <class T>
void
swap(T& x, T& y)
{
printf("generic exp::swap\n");
T tmp = x;
x = y;
y = tmp;
}
template <class T>
void algorithm(T* begin, T* end)
{
if (end-begin >= 2)
exp::swap(begin[0], begin[1]);
}
}
// simulate user code which includes <algorithm>
struct A
{
};
namespace exp
{
void swap(A&, A&)
{
printf("exp::swap(A, A)\n");
}
}
// exercise simulation
int main()
{
A a[2];
exp::algorithm(a, a+2);
}对我来说,这是打印出来的:
generic exp::swap如果你的编译器打印出一些不同的东西,那么它就没有正确地实现模板的“两阶段查找”。
如果您的编译器符合(C++98/03/11中的任何一个),那么它将给出与我所显示的相同的输出。在这种情况下,你担心会发生的事情就会发生。将您的swap放入名称空间std (exp)并没有阻止这种情况的发生。
Dave和我都是委员会成员,并且已经在这个标准领域工作了十年(并不总是彼此达成一致)。但这个问题已经解决了很长一段时间,我们都同意如何解决这个问题。忽略Dave在这一领域的专家意见/答案,后果自负。
这个问题是在C++98发布后曝光的。大约从2001年开始,我和戴夫开始使用work this area。这就是现代的解决方案:
// simulate <algorithm>
#include <cstdio>
namespace exp
{
template <class T>
void
swap(T& x, T& y)
{
printf("generic exp::swap\n");
T tmp = x;
x = y;
y = tmp;
}
template <class T>
void algorithm(T* begin, T* end)
{
if (end-begin >= 2)
swap(begin[0], begin[1]);
}
}
// simulate user code which includes <algorithm>
struct A
{
};
void swap(A&, A&)
{
printf("swap(A, A)\n");
}
// exercise simulation
int main()
{
A a[2];
exp::algorithm(a, a+2);
}输出为:
swap(A, A)更新
有一项观察表明:
namespace exp
{
template <>
void swap(A&, A&)
{
printf("exp::swap(A, A)\n");
}
}成功了!那么为什么不使用它呢?
考虑一下您的A是一个类模板的情况:
// simulate user code which includes <algorithm>
template <class T>
struct A
{
};
namespace exp
{
template <class T>
void swap(A<T>&, A<T>&)
{
printf("exp::swap(A, A)\n");
}
}
// exercise simulation
int main()
{
A<int> a[2];
exp::algorithm(a, a+2);
}现在它又不能工作了。:-(
因此您可以将swap放入名称空间标准中并使其正常工作。但是当你有一个模板:A<T>的时候,你需要记住把swap放在A的命名空间中。而且因为如果你把swap放在A的命名空间中,这两种情况都可以工作,所以更容易记住(也更容易教别人)只用一种方式就可以了。
发布于 2008-08-14 19:46:32
根据C++标准,您不允许重载std::swap,但是明确允许您将自己类型的模板专门化添加到std名称空间。例如。
namespace std
{
template<>
void swap(my_type& lhs, my_type& rhs)
{
// ... blah
}
}那么std容器(和其他任何地方)中的用法将选择您的专门化,而不是通用的专门化。
还要注意的是,为您的派生类型提供交换的基类实现还不够好。例如,如果你有
class Base
{
// ... stuff ...
}
class Derived : public Base
{
// ... stuff ...
}
namespace std
{
template<>
void swap(Base& lha, Base& rhs)
{
// ...
}
}这将适用于基类,但如果您尝试交换两个派生对象,它将使用来自std的泛型版本,因为模板化的交换是完全匹配的(并且它避免了仅交换派生对象的“基”部分的问题)。
注意:我已经更新了这个,去掉了我上一个答案中的错误部分。哦!(感谢puetzk和j_random_hacker指出)
https://stackoverflow.com/questions/11562
复制相似问题