为什么有那么多的std :: swap的特化?

在查看std::swap的文档时,我看到了很多专业化的东西。
它看起来像每个STL容器,以及许多其他标准设施有一个专门的交换。
我想借助模板,我们不需要所有这些专业化?

例如,
如果我写我自己的pair它与模板版本正常工作:

 template<class T1,class T2> struct my_pair{ T1 t1; T2 t2; }; int main() { my_pair<int,char> x{1,'a'}; my_pair<int,char> y{2,'b'}; std::swap(x,y); } 

那么从专门的std::pair获得什么呢?

 template< class T1, class T2 > void swap( pair<T1,T2>& lhs, pair<T1,T2>& rhs ); 

我还想知道是否应该为自定义类写作自己的专业化,
或者仅仅依靠模板版本。

那么从专门的std :: pair中获得什么呢?

性能。 通用交换通常是足够好的(自C ++ 11以来),但很less优化(对于std::pair以及大多数其他数据结构)。

我也想知道是否应该为自定义类编写我自己的专业化版,或只是依靠模板版本。

我build议默认情况下依靠模板,但如果分析显示它是一个瓶颈,知道可能有改进的余地。 不成熟的优化和所有的…

std::swap是按照下面的代码来实现的:

 template<typename T> void swap(T& t1, T& t2) { T temp = std::move(t1); t1 = std::move(t2); t2 = std::move(temp); } 

(有关更多信息,请参阅“标准库如何实现std :: swap?” )。

那么从专门的std::pair获得什么呢?

std::swap可以通过以下方式进行专门化( 从libc++简化 )

 void swap(pair& p) noexcept(is_nothrow_swappable<first_type>{} && is_nothrow_swappable<second_type>{}) { using std::swap; swap(first, p.first); swap(second, p.second); } 

正如你所看到的,使用ADL直接调用这个对的元素:这样就可以在firstsecond上使用自定义和可能更快的swap 实现(这些实现可以利用元素内部结构的知识来获得更多性能)

(请参阅“如何using std::swap启用ADL?”以获取更多信息。)

大概这是出于性能方面的原因,如果这个pair包含的types是便宜的交换,但昂贵的复制,像vector 。 由于它可以调用firstsecond交换,而不是使用临时对象进行复制,因此可以显着改善程序性能。

交换两对的最有效方式与交换两个向量的最有效方式不同。 这两种types有不同的实现,不同的成员variables和不同的成员函数。

没有一种通用的方式来以这种方式“交换”两个对象。

我的意思是,当然,对于可复制的types,你可以这样做:

 T tmp = a; a = b; b = tmp; 

但这是可怕的。

对于一个可移动的types,你可以添加一些std::move并防止副本,但是你仍然需要在下一层“交换”语义,以便实际上具有有用的移动语义。 在某个时候,你需要专注。

性能的原因是,特别是前C + + 11。

考虑一下像“vector”types的东西。 Vector有三个字段:大小,容量和指向实际数据的指针。 这是复制构造函数和复制分配复制实际的数据。 C ++ 11版本还有一个移动构造函数和移动赋值来窃取指针,将源对象中的指针设置为null。

专用的vector交换实现可以简单地交换字段。

基于复制构造函数,复制赋值和析构函数的通用交换实现将导致数据复制和dynamic内存分配/解除分配。

基于移动构造函数,移动赋值和析构函数的通用交换实现将避免任何数据复制或内存分配,但会留下一些优化器可能或可能无法优化的冗余空值和空值检查。


那么为什么有一个专门的交换实施“对”? 对于一对int和char是没有必要的。 他们是普通的旧数据types,所以通用的交换就好了。

但是如果我有一对说Vector和String呢? 我想为这些types使用专家交换操作,所以我需要对处理它的对types进行交换操作,通过交换它的组件元素。

有一个规则(我认为它来自Herb Sutter的Exceptional C ++或Scott Meyer的Effective C ++系列),如果你的types可以提供一个不抛出的交换实现,或者比通用的std::swap函数更快,它应该做为成员函数void swap(T &other)

理论上,通用的std::swap()函数可以使用模板魔法来检测成员交换的存在,并调用它来代替

 T tmp = std::move(lhs); lhs = std::move(rhs); rhs = std::move(tmp); 

但似乎没有人想到这个问题,但是,为了调用(可能更快)的成员交换,人们往往会增加免费swap重载。