实现一个不抛异常的swap函数

2018-08-31  本文已影响0人  wayyyy
template<typename T>
void swap(T& a, T& b)
{
    T temp(a);  // 拷贝构造
    a = b;      // 拷贝赋值运算符
    b = temp;   // 拷贝赋值运算符
}

一个基本的swap函数大概如上所述,但是对某些类型而言。典型比如:pimpl手法(pointer to implementation),这些复制没有必要。

class WidgetImpl
{
    public:
        ...
    private:
        int a, b, c;
        std::vector<double> v;    // 意味着拷贝开销很大
};

class Widget
{
    public:
        Widget(const Widget& rhs);
        Widget& operator=(const Widget& rhs)
        {
            ...
            *pImpl = *(rhs.pImpl);
        }
    private:
        WidgetImpl* pImpl;
}

一旦需要置换两个Widget对象值,我们只需要交换pImpl指针,但缺省的swap算法不知道这一点,它不止拷贝三个Widget,还拷贝三个WidgetImpl对象。

我们希望能够告诉std::swap,当Widgets被置换时真正该做的是置换其内部的pImpl指针,确切的做法就是:将std::swap针对Widget特化。

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

通常我们不被允许改变std命名空间内的任何东西,但可以为标准的template制造特化版本。
虽然上面的代码看起来达到我们的目的,但实际上这个函数无法通过编译,因为它企图访问 a 和 b 内的Impl指针,而那是private的。
但我们可以在内部实现一个swappublic成员函数做真正的置换工作,然后将std::swap特化,令它调用成员函数:

class Widget
{
    public:
        ...
        void swap(Widget& other)
        {
            using std::swap;
            swap(pImpl, other.pImpl);
        }
    ...
};

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

这种做法不仅能通过编译,还与STL容器有一致性,因为所有的STL容器也都提供有public swap成员函数和std::swap特化版本。

总结
上一篇 下一篇

猜你喜欢

热点阅读