为什么每个STL容器都定义了一个作为成员函数的交换函数?

11

考虑STL中的queue容器。

据我了解,在<algorithm>标头中可用的swap()函数可以正常工作。

我了解到swap()只会对queue实例进行表面复制,即仅会复制frontrear指针以及size和其他数据成员。

两个队列中的条目不会物理交换位置,但无论何种情况,一旦交换指针和大小,这两个队列就会被有效地交换。


3
通常情况下,如果某个STL容器具有一个成员函数,其功能也可以通过<algorithm>获得,那么意味着STL容器可以更快地执行该功能,因为它了解数据的结构。 - Algirdas Preidžius
3个回答

4
在C++11引入移动语义之前,std::swap的通用实现没有选择只能进行两次复制。从概念上讲,这个过程是这样的:
template <class T>
void swap(T &a, T &b)
{
  T t(a);
  a = b;
  b = t;
}

请注意,这个通用的std::swap不知道传入对象的内部情况(因为它可以被任意用户类型调用),因此需要进行拷贝。对于容器来说,这意味着拷贝元素。
提供一个优化后的成员函数swap只需重新指向一些内部指针,因此可以极大地提高性能。
自从移动语义被引入以来,使用移动操作可以使通用swap更加高效。再次强调概念上:
template <class T>
void swap(T &a, T &b)
{
  T t(::std::move(a));
  a = ::std::move(b);
  b = ::std::move(t);
}

当然,在实践中,它可能对涉及移动操作的要求是非抛出的,并且还有各种额外的位。
在移动语义已经实现的情况下,优化后的成员版本可能比以前不那么重要。但是,如果了解类型的确切实现细节,则交换它可能比三个通用移动更快。
此外,请注意,几乎所有在标准库中定义的类型都存在特定于类型的std :: swap重载。这些重载的作用是简单地在一个操作数上调用优化的swap成员函数。这样,你既拥有通用的自由函数swap,可以用于任何事物,又有针对标准库所知道的所有内容的优化实现。
可以放弃成员函数并直接提供优化实现的std :: swap重载,但这意味着它们可能需要成为友元,并且可能被视为不太容易被用户代码访问。

2
然而,成员swap可以比3次移动的std::swap更有效率。 - MSalters
@MSalters 我正在输入你评论的那个补充内容。 - Angew is no longer proud of SO
@Angew:真的吗?嗯...从未听说过。 - Lightness Races in Orbit
1
@LightnessRacesinOrbit 请查看专业领域:http://en.cppreference.com/w/cpp/algorithm/swap - Richard Critten
在标准中,重载函数被定义在每个容器等定义的旁边。例如,C++11 23.3.5.6 [list.special] 是针对 list 的。 - Angew is no longer proud of SO
显示剩余5条评论

3
自由的 std::swap 对每个容器都有重载,因此您是正确的,成员函数 swap 不是必需的。自由重载可以被声明为 friend 并执行所有实现特定、容器特定的魔法以实现高效的交换。
目前,它们调用成员 swap。我想这样做允许以不同的方式调用这些函数,并且节省了许多 friend 声明。
我不知道除此之外还有什么。

1
统一调用语法 提案(由Bjarne Stroustrup于2014年版本)最终被采纳并进行了一些最后的微调后,您的问题将变得无关紧要,因为std::swap(my_queue, another_queue)my_queue.swap(other_queue)可能是同一个函数的别名。不幸的是,这在C++17中不会发生。也许在C++20中?人们可以梦想...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接