考虑写一个不抛异常的swap函数

版权声明:转载请注明出处,谢谢!!! https://blog.csdn.net/qhdhnbd110/article/details/83750153

swap函数存在于STL中,其典型实现如下:

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

这个函数执行了对象a复制到temp,b复制到a,Temp复制到b,但是对于某些情况,多余的复制是没有必要的:

考虑下面的例子:

class WidgetImpl
{
public:
    ...
private:
    int a, b, c;
    std::vector<double> v;
};
class Widget
{
    Widget(const Widget &rhs);
    Widget &operator =(const Widget &rhs);
private:
    WdigetImpl *pImpl;
};

        一旦要交换两个Widget对象的值,我们仅仅需要的就是置换其pImpl指针,但缺省的算法不止复制三个Widgets,还复制三个WidgetImpl对象,非常低效。

      我们需要告诉缺省的swap函数,只需要交换两个指针就好,为了实现这个思路,考虑如下做法:

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

        这个做法叫做函数的特化https://mp.csdn.net/postedit/83651232

但是上述做法并不能通过编译,因为pImpl是类的私有成员,我们无法在类外调用。

既然如此,那么我们就让特化的swap函数成为Widget类的类成员:

class Widget
{
public:
...
    Widget(const Widget &rhs);
    Widget &operator =(const Widget &rhs);
public:
    void swap(Widget &Other)
    {
        using std::swap;
        swap(pImpl, Other.pImpl);
    }
private:
    WdigetImpl *pImpl;
};
namespace std
{
    template <>
    void swap<Widget>(Widget &a, Widget &b)
    {
        a.swap(b);
    }
}

 这样,就可以通过编译。

上述做法是基于类都是class而不是template class,下面考虑一下如果类都是template class会发生什么:

template <typename T>
class WidgetImpl{...};
template <typename T>
class Widget{...};

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

这种情况是不能通过编译的,因为函数是不能够被偏特化的,只有类才能偏特化。

一般情况下,偏特化一个函数的代替做法为重载函数:

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

但是这也是不合法的,因为命名空间std内是禁止新的东西的,你可以全特化,但不可以重载。

添加一个新的命名空间可以解决这种局面:

namespace WidgetStuff
{
    template <typename T>
    class WidgetImpl{...};
    template <typename T>
    class Widget{...};   //包括成员函数swap


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

这样一来,c++的名称查找法则会找到WidgetStuff内的Widget专属版本。

这个做法既适用于class也适用于template class,但你最好还是为swap函数在命名空间std中实现一个特化的版本:

template <typename T>
void DoSomething(T &a, T &b)
{
    ...
    using std::swap;
    swap(a, b);
    ...
}

        如果这样调用swap函数,那么如果编译器找不到针对T在某个命名空间内的专属版本,就会调用std内的默认版本,但是如果这样调用:

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

那么就会直接调用std中的默认版本, 所以为了使这种错误的调用方式也可以调用适当的swap函数,你可以在std中实现一个全特化的版本,这样编译器会首先调用全特化版本。

总结:

如果std版本的效率满足你的要求,可以什么都不做,否则:

1. 在类中提供一个public function高效的置换;

2. 在class或template class所在的命名空间提供一个non-member函数去调用成员函数;

3. 如果你编写的是class而不是template class,为你的class特化std::swap。

最后,注意函数的调用方式。

还有一点:成员版swap函数不应该抛出异常。 

猜你喜欢

转载自blog.csdn.net/qhdhnbd110/article/details/83750153
今日推荐