swap 就是交换,当我们初学 C语言 或者 C++ 的时候,我们通常会自己写一个类似于 swap 功能的函数
后文将采取C++ template相关代码进行介绍(书中也是如此),如果不明白 C++ 函数模版、类模版、全特化、偏特化相关概念的话,请百度搜索一下。
(资料图片)
这里只需要类型 T 是支持 copy constructor 和 copy assignment 就能行。这种版本对于内置类型(int、double……)很好用,但是对于自定义的类来说就不一定了,如下
我们现在为 Widget 定义 swap 函数,不需要交换所有内容,只需要交换其中的 pImpl ,但是缺省的 swap 函数不知道这点,会复制三个 Widget 和三个 WidgetImpl ,效率低下。
本例子表明,当模版类型是 Widget 的时候调用属于 Widget 的专属代码,否则调用一般的 swap 代码。本段代码不能通过是因为 pImpl 是 private,不能被 non-member non-friend 函数调用。我们可以将这个版本的特化声明为 friend ,但不符合以往的实现方式。采取如下方法
这种方式实现可以通过编译还可以与 STL 容器保持一致性,因为所有 STL 容器都提供 public swap 函数和 std::swap特化版本。调用前者更好。
现在假设 Widget 不是 class 而是 class template
这样就无法通过编译,因为这样做是对 function template 偏特化,C++不允许,C++只允许对 class template 偏特化。如果打算对 function template 偏特化,只需要简单的为其设置一个重载版本
这段代码能够通过编译,但是最好不要放在 std 空间下,因为这不符合 C++ 标准委员会的决定。我们最好放在自己声明的命名空间下。
编译器会优先在 global 和 class 所在的 namespace 寻找关于类型 T 的 swap 特化函数,如果找到了就使用,如果没找到,就只能调用一般的 swap 函数。
总结:
当 std::swap 对你的类型效率不高时,提供一个 swap 成员函数,并确定这个函数不抛出异常。
如果你提供一个 member swap,也应该提供一个 non-member swap 用来调用前者。对于 classes (而非 templates),也应该特化 std::swap。
调用 swap 时应该针对 std::swap 使用 using::std 声明式,然后调用 swap 并且不带任何命名空间资格修饰。
为 “用户定义类型” 进行 std template 全特化是好的,但是千万不要在 std 内加入某些对 std 而言全新的东西。