C++ swap函数(effective C++ 条款25)

来源:互联网 发布:双色球红球246算法 编辑:程序博客网 时间:2024/06/05 01:13

标准库swap函数

我们首先看一下标准库的swap函数是如何实现的,它是一个定义在std命名空间的函数模板:

namespace std {    template<typename T>    void swap(T &a,T &b) {        T temp(a);        a = b;        b = temp;    }}

可以看到,标准库的swap实现调用了拷贝构造函数(对于非内置类型),并且有两次的赋值运算,这在很多情况下是不满足我们的效率需求的(大型对象的拷贝是毫无必要的操作,我们仅需要交换指针,若存在容器类型,调用容器类的swap明显是更加正确的操作)。在C++11中,我们有如下的优化:

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

移动方式的本质就是移交临时对象对资源的控制权,通常就是指针的替换,因此上述操作对存在移动构造和移动赋值运算的类来讲,已经可以基本满足要求,但是,对于未定义上述操作的类来讲,改进版本的swap操作并未有任何效率上的提升,因此,有必要定义类类型的swap。

copy and swap中的 swap操作

前一篇文章中关于copy and swap操作中提到了swap操作,作为成员函数的swap交换了两者指针:

class A {private:int *a;public:void swap(A& rhs) {    using std::swap;    swap(this->a,rhs.a);// 我们简单地交换了指针}};

我们稍后将解释关于using std::swap为何是必要的。

上述操作并没有解决问题,我们希望能够像调用普通swap函数操作一样调用swap(A &a,A&b),因此,我们下一步的操作就是在std命名空间内特化swap版本:

namespace std {    template<>    void swap<A>(A&a,A&b) {        a.swap(b);    }}

在std空间内的特化版本满足C++标准的规定,这种扩充操作使得我们的特化版本对包含了std空间的文件都处于可见状态,因此,我们可以像以前一样使用swap进行交换:

using std::swap;A a,b;swap(a,b);

我们知道,C++允许对类模板全特化,但是对函数模板不允许全特化:

template<typename T>// 以下定义不允许!void swap<A<T>> (A<T>& a,A<T>& b) {    a.swap(b);  }

同样的,std内对swap的重载也不符合规定,我们的解决方案就是,在自定义的命名空间内定义swap函数:

namespace Astuff {    template<typename T>    class A {    ...    ...    };    template<typename T>    void swap(A<T>&a,A<T> &b) {        a.swap(b);    }};

这样,我们在当前的命名空间内就拥有了swap的完整定义,那么为什么在使用时要加using std::swap呢?这句声明会使std命名空间的swap暴露出来,编译器会自动在当前命名空间和std空间内寻找最符合当前函数调用的swap版本,因此,以下的写法完全错误:

A a,b;std::swap(a,b);

这种写法直接调用了std空间内的swap函数,因此并不符合大多数情况下的需求,using版本才是最准确的版本:

using std::swap;swap(a,b);