考虑如下代码
class WidgetImp1 { //针对Widget数据设计的class,细节不重要
public:
//...
private:
int a, b, c;
std::vector<double> v;
};
class Widget {
public:
Widget(const Widet& rhs);
Widget& operator=(const Widget& rhs) {
//...
*pImpl = *(rhs.pImpl);
}
private:
WidgetImpl* pImpl;
};
当我们要交换Widget对象值的时候,我们唯一要做的就是交换其成员pImpl指针,但是缺省的swap算法不知道这一点,不但会复制三个widget,还会复制三个WidgetImpl对象,效率十分低下;
我们希望告诉std::swap,当交换Widget时,真正要做的是交换内部的pImpl指针;办法就是将std::swap针对Widget特化
namespace std {
template<>
void swap<Widget> (Widget& a, Widget& b){
swap(a.pImpl, b.pImpl);
}
}
template<>代表它是针对std::swap的一个全特化,函数名称之后的<Widget>代表这一特化版本系针对"T是Widget"而设计的;通常我们不能够(不被允许)改变std命名空间内的任何东西,但是可以(被允许)为标准templates(如swap)制造特化版本;
上述特化swap还无法通过编译,因为pImpl是私有成员,无法访问,解决办法是令Widget声明一个名为swap的public成员函数做真正的交换工作,然后做特化工作
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);
}
}
现在我们假设Widget和WidgetImpl都是class templates而非classes:
template<typename T>
class WidgetImpl {//...}
template<typename T>
class Widget {//...}
我们可以像之前一样在Widget放个swap成员函数,但是在特化std::swap时遇到了麻烦:
namespace std {
template<typename T>
void swap< Widget<T> >(Widget<T>& a, Widget<T>& b){ // 错误!
a.swap(b);
}
}
我们企图偏特化一个function template,但是C++之允许对class templates偏特化;当你打算偏特化一个function template时惯常做法是简单地为它添加一个重载版本:
namespace std {
template<typename T>
void swap(Widget<T>& a, Widget<T>& b){ // 重载版本
a.swap(b);
};
}
一般而言,重载function template没有问题,但是std是个特殊的命名空间,客户可以全特化std内的template但不可以添加新的templates到std里头;解决办法是我们还是声明一个 non-member的swap让它调用member的swap,但不再将那个non-member声明为std::swap的特化版本:
namespace WidgetStuff {
// ...
template<typename f>
class Widget{ //...};
//...
template<typename T>
void swap(Widget<T>& a, Widget<T>& b){ // 这里并不属于std命名空间
a.swap(b);
};
}
最后调用swap时应该针对std::swap使用using声明式,然后调用swap并且不带任何"命名空间资格修饰",如下:
template<typename T>
void doSomething(T& a,T& b) {
using std::swap // 令std::swap在此函数内可用
//...
swap(a, b);
//...
}
本文探讨了在C++中如何为特定类(如Widget)特化std::swap以提高效率,避免不必要的对象复制。通过在Widget类中添加swap成员函数并创建非成员swap函数来调用成员函数,实现了对Widget对象的高效交换。在处理模板类Widget和WidgetImpl时,面临了特化和访问权限的问题,并提供了相应的解决方案。
2379

被折叠的 条评论
为什么被折叠?



