class WidgetImpl {...}; template
class Widget {...}; ... template
void swap(Widget
& a, Widget
& b) { a.swap(b); } }
现在,任何时候如果打算置换两个Widget对象,因而调用swap,C++的名称查找法则都会找到WidgetStuff内的Widget专属版本。
这个做法对class和class template都行得通。如果你想让你的”class“专属版swap在尽可能多的语境下被调用,你需要同时在该class所在命名空间内写一个non-member版本以及一个std::swap特化版本。
另外,如果没有像上面那样额外使用某个命名空间,上述每件事情仍然使用。但你又何必再global命名空间里面塞这么多东西呢?
补充思考
目前提到得都是和swap编写有关的。现在我们换位思考,从客户观点看看问题。假设我们需要写一个function template:
template
void doSomething(T& obj1, T& obj2) { ... swap(obj1, obj2); ... }
此时swap是调用哪个版本呢?我们当然希望是调用T专属版本,并且在该版本不存在的情况下,调用std内的一般化版本。
template
void doSomething(T& obj1, T& obj2) { using std::swap; ... swap(obj1, obj2); // 为T类型对象调用最佳swap版本。 ... }
C++名称查找法则确保将找到global作用域或T所在命名空间内的任何T专属的swap。如果T是Widget并位于命名空间WidgetStuff内,编译器会使用”实参取决之查找规则“找出WidgetStuff内的swap。如果没有T专属之swap存在,编译器就使用std内的swap。
以下是我设计的一个不大合乎逻辑的代码,但证明了上述说法是合理的。
#include
using namespace std; namespace test { class trys { public: void swap(trys &one, trys &two) { cout << "yes!" << endl; } }; void swap(trys &one, trys &two) { cout << "yes!" << endl; } } int main(int argc, const char * argv[]) { // insert code here... test::trys a; int b = 12; { using std::swap; swap(b, b); swap(a, a); } return 0; } /* yes! Program ended with exit code: 0 */
总结:
如果swap缺省实现版的效率不足,(那几乎意味着你的class或template使用了某种pimpl手法),可以试着做以下事情:
提供一个public swap成员函数,让他高效地置换你的类型的两个对象值。 在你的class或template所在的命名空间内提供一个non-member swap,并命它调用上述swap成员函数。 如果你在编写一个class,并为你的class特化std::swap,并令他调用你的swap成员函数。
最后,如果你调用swap,请确保包含一个using声明式。
补充内容:(全特化和偏特化)
模板为什么要特化,因为编译器认为,对于特定的类型,如果你能对某一功能更好的实现,那么就该听你的。
模板分为类模板与函数模板,特化分为全特化与偏特化。全特化就是限定死模板实现的具体类型,偏特化就是如果这个模板有多个类型,那么只限定其中的一部分。
先看类模板:
template
class Test { public: Test(T1 i,T2 j):a(i),b(j){cout<<"模板类"<
class Test
{ public: Test(int i, char j):a(i),b(j){cout<<"全特化"<
class Test
{ public: Test(char i, T2 j):a(i),b(j){cout<<"偏特化"<
那么下面3句依次调用类模板、全特化与偏特化:
Test
t1(0.1,0.2); Test
t2(1,'A'); Test
t3('A',true);
而对于函数模板,却只有全特化,不能偏特化:
//模板函数 template
void fun(T1 a , T2 b) { cout<<"模板函数"<
void fun
(int a, char b) { cout<<"全特化"<
void fun
(char a, T2 b) { cout<<"偏特化"<
至于为什么函数不能偏特化,似乎不是因为语言实现不了,而是因为偏特化的功能可以通过函数的重载完成。