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

时间:2017-03-10 12:21:34

标签: c++ stl

考虑STL中的queue容器。

据我了解,swap()标题中提供的<algorithm>可以正常使用。

我了解swap()只会表面复制queue个实例,也就是说,只会复制frontrear个指针以及{{1}和其他数据成员。

两个队列中的条目不会在物理上交换位置,但我不明白为什么在任何情况下这都是必要的,因为一旦指针和大小被交换,两个队列将被有效地交换。

3 个答案:

答案 0 :(得分:3)

在C ++ 11引入移动语义之前,std::swap的通用实现别无选择,只能做两个副本。从概念上讲,这是:

template <class T>
void swap(T &a, T &b)
{
  T t(a);
  a = b;
  b = t;
}

请注意,此通用std::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重载内提供优化的实现,但这意味着他们可能需要被贬低并且可能被认为更难以被用户访问代码。

答案 1 :(得分:2)

由于免费std::swap对每个容器都有重载,因此您确实不需要成员函数swap。可以声明friend的自由重载并执行所有特定于实现的特定于容器的魔法,以实现有效的交换。

实际上,他们会调用成员swap。我想这可以为调用这些函数提供额外的灵活性,并且可以节省大量的friend声明。

我不知道还有什么比这更重要。

答案 2 :(得分:0)

当最终采用uniform call syntax proposal(2014年Bjarne Stroustrup版本)时,经过一些最后的调整,你的问题将在std::swap(my_queue, another_queue)和{{1}的意义上变得毫无意义可能是完全相同的函数的别名。不幸的是,C ++ 17不会发生这种情况。也许在C ++ 20中?人们可以梦想......