25-swap

51 阅读2分钟

1.普通的交换

class WidgetImpl1 {
public:
private:
    int a , b, c;
    std::vector<double> v;
};

class Widget1 {
public:
    Widget1(const Widget1& rhs) = default;
    Widget1& operator=(const Widget1& rhs)
    {
        *pImpl = *(rhs.pImpl);
        return *this;
    }
private:
    WidgetImpl1 * pImpl;
};

void dosomesing(Widget1 &a, Widget1&b)
{
    using namespace std;
    swap(a,b);
}

问题:真正需要置换的是pimpl,如果用默认的std::swap, 置换了三次Widget,解决方案特化std::swap函数

2、全特化std::swap,需要访问私有变量

class WidgetImpl2 {
public:
private:
    int a , b, c;
    std::vector<double> v;
};


class Widget2 {
public:
    Widget2(const Widget2& rhs) = default;
    
    Widget2& operator=(const Widget2& rhs)
    {
        *pImpl = *(rhs.pImpl);
        return *this;
    }
private:
    
    WidgetImpl2 * pImpl;
};

/*
namespace std {
template <>
void swap<Widget2> (Widget2& a, Widget2& b) {
    // 编译不通过, 访问了私有的pImpl,错误信息: 'pImpl' is a private member of 'Widget2'
    swap(a.pImpl, b.pImpl) ;
}
}; //end std
void dosomesing(Widget2 &a, Widget2&b)
{
    using namespace std;
    swap(a,b);
}
*/

全特化的规律: <>中的是空的, swap后面是  目前这个版本无法编译过,应为pImpl是私有成员,解决办法有:  1、 将pImpl公开  2、将swap声明为frend  3、另Widget生成一个swap函数,本次采用的方案

3、widget内部总结swap函数,std::swap调用WidgetImpl3的swap函数;如果widget是模版,函数不能偏特化

class WidgetImpl3 {
public:
private:
    int a , b, c;
    std::vector<double> v;
};

template <typename T>
class Widget3 {
public:
    Widget3(const Widget3& rhs) = default;
    
    Widget3& operator=(const Widget3& rhs)
    {
        *pImpl = *(rhs.pImpl);
        return *this;
    }
    
    void swap( Widget3& rhs)
    {
        using std::swap;
        swap(this->pImpl, rhs.pImpl);
    }
private:
    
    WidgetImpl3 * pImpl;
};

/*
namespace std {
template <typename T>
    void swap<Widget4<T>> (Widget4<T>& a, Widget4<T>& b) {
        //Function template partial specialization is not allowed
    }
};
 */

/*
 上面的代码如果Widget是模版的话,无法编译通过。因为函数不能偏特化, 原因是重载更复杂了。
 */

// 打算偏特化, 惯例的做法是提供一个重载的版本
namespace std {
template <typename T>
void swap (Widget3<T>& a, Widget3<T>& b) {
    a.swap(b);
}
};

4、放到命名空间中

namespace WidgetStuff {


class WidgetImpl4 {
public:
private:
    int a , b, c;
    std::vector<double> v;
};

template <typename T>
class Widget4 {
public:
    Widget4(const Widget4& rhs) = default;
    
    Widget4& operator=(const Widget4& rhs)
    {
        *pImpl = *(rhs.pImpl);
        return *this;
    }
    
    void swap( Widget4& rhs)
    {
        using std::swap;
        swap(this->pImpl, rhs.pImpl);
    }
private:
    
    WidgetImpl4 * pImpl;
};

template<typename T>
void swap(Widget4<T> &a, Widget4<T>& b)
{
    a.swap(b);
};

// argument-dependent lookup
} //end WidgetStuff

通用的思路:

  • 类内定义一个swap函数
  • 命名空间总提供一个swap函数