條款41:了解隱式接口和編譯器多態(tài)
- 對(duì)于class而言,接口是顯式的,動(dòng)態(tài)通過(guò)virtual函數(shù)實(shí)現(xiàn),發(fā)生于運(yùn)行期間。
- 對(duì)于template而言,接口是隱式的,通過(guò)模板實(shí)例化和函數(shù)重載解析實(shí)現(xiàn),發(fā)生于編譯期。
條款42:了解typename的雙重意義
- 聲明template參數(shù)時(shí),typename和class可以互換。
- 需要使用typename關(guān)鍵字表示嵌套從屬類型名稱,如下示例代碼:
#include <iostream>
#include <vector>
template <typename T>
void Print2nd(const T& container)
{
if (container.size() >= 2) {
typename T::const_iterator iter(container.begin()); //這里必須加typename表示T::const_iterator是一個(gè)名稱
iter++;
int value = *iter;
std::cout << value << std::endl;
}
}
其中T::const_iterator就是嵌套從屬類型,必須使用typename關(guān)鍵字。
條款43:學(xué)習(xí)處理模板化基類內(nèi)的名稱
考察以下示例代碼:
#include <iostream>
template <typename T>
class B {
public:
void Foo() {std::cout << "B::Foo" << std::endl;}
};
template <typename T>
class D : public B<T> {
public:
void Test() {B<T>::Foo();} // 這里必須在Foo()名稱前顯式指明使用基類名稱B<T>
};
int main()
{
D<int> obj;
obj.Test();
return 0;
}
繼承一個(gè)模板基類時(shí),如果在子類的成員函數(shù)中調(diào)用基類中的函數(shù),則必須使用“this->”或者“基類名稱::”,顯式的指定函數(shù)是來(lái)自基類的,否則編譯器不知道該函數(shù)來(lái)自哪里,相當(dāng)于顯式的觸發(fā)基類模板的實(shí)例化。
條款44:與參數(shù)無(wú)關(guān)的代碼抽離出template
主要作用是防止模板實(shí)例化后代碼膨脹問(wèn)題。
- template代碼不應(yīng)該與某個(gè)造成膨脹的template參數(shù)產(chǎn)生相依關(guān)系。
- 可以使用函數(shù)參數(shù)或者類成員變量替換template參數(shù)。
條款45:成員函數(shù)模板接受所有兼容類型
比如C++11中的智能共享指針,在實(shí)現(xiàn)基類和子類對(duì)應(yīng)的智能指針轉(zhuǎn)換時(shí)即是這種情況,如下所示:
#include <iostream>
#include <memory>
template <typename T>
class SmartPtr {
public:
SmartPtr<T>(T* p) : ptr(p) {}
template <typename C>
SmartPtr<T>(const SmartPtr<C>& other) {ptr = (T*)(other.get());}
T* get() const {return ptr;}
private:
T* ptr;
};
class B {
public:
virtual ~B() {}
};
class D : public B {
};
int main()
{
SmartPtr<D> sp(new D());
SmartPtr<B> sp1 = sp;
return 0;
}
對(duì)于“SmartPtr<B> sp1 = sp;”,它調(diào)用的是SmartPtr<B>的拷貝構(gòu)造函數(shù),參數(shù)是SmartPtr<D>類型。
條款46:需要類型轉(zhuǎn)換時(shí)請(qǐng)為模板定義非成員函數(shù)
參考以下示例代碼:
- 版本1:在模板類內(nèi)定義operator成員
#include <iostream>
template <typename T>
class R {
public:
R(T x) : val_(x) {}
R<T> operator*(const R<T>& rhs) {
int value = this->val_ * rhs.val_;
return R(value);
}
void Print() {std::cout << val_ << std::endl;}
public:
T val_;
};
int main()
{
R<int> r1(10), r2(12);
R<int> res1 = r1 * r2;
res1.Print(); // 編譯OK
R<int> res2 = r1 * 2;
res2.Print(); // 編譯OK
R<int> res3 = 2 * r2;
res3.Print(); // 編譯不通過(guò)
return 0;
}
在這個(gè)版本中我們定義了一個(gè)模板類內(nèi)的operator,可以看到當(dāng)編譯"2 * r2"這條語(yǔ)句時(shí),編譯不通過(guò)。因?yàn)榫幾g器遇到2這個(gè)常量時(shí),它無(wú)法知道將它轉(zhuǎn)換為一個(gè)R實(shí)例對(duì)象。所以,必須定義一個(gè)非成員函數(shù)。
- 版本2:在類模板外定義一個(gè)非成員operator。
#include <iostream>
template <typename T>
class R {
public:
R(T x) : val_(x) {}
void Print() {std::cout << val_ << std::endl;}
public:
T val_;
};
template <typename T>
R<T> operator*(const R<T>& lhs, const R<T>& rhs)
{
return R<T>(lhs.val_ * rhs.val_);
}
int main()
{
R<int> r1(10), r2(12);
R<int> res1 = r1 * r2;
res1.Print(); // 編譯OK
R<int> res2 = r1 * 2;
res2.Print(); // 編譯不通過(guò)
R<int> res3 = 2 * r2;
res3.Print(); // 編譯不通過(guò)
return 0;
}
可以看到這次r1 * 2也編譯不通過(guò)了,這是因?yàn)槟0搴瘮?shù)不接受任何隱式的轉(zhuǎn)換。
- 版本3:在模板類內(nèi)定義一個(gè)friend函數(shù)
#include <iostream>
template <typename T>
class R {
public:
R(T x) : val_(x) {}
void Print() {std::cout << val_ << std::endl;}
friend R<T> operator*(const R<T>& lhs, const R<T>& rhs)
{
return R<T>(lhs.val_ * rhs.val_);
}
public:
T val_;
};
int main()
{
R<int> r1(10), r2(12);
R<int> res1 = r1 * r2;
res1.Print(); // 編譯OK
R<int> res2 = r1 * 2;
res2.Print(); // 編譯OK
R<int> res3 = 2 * r2;
res3.Print(); // 編譯OK
return 0;
}
條款47:請(qǐng)使用traits class表現(xiàn)類型信息
一句話總結(jié):traits class是“type of type”,我們常用的std::is_pod就是這類class,它是編譯器執(zhí)行的。
#include <iostream>
#include <type_traits>
int main()
{
int a;
std::cout << std::is_pod<decltype(a)>::value << std::endl;
return 0;
}
條款48:認(rèn)識(shí)template元編程
- 模板元編程(TMP)可以將工作由運(yùn)行期移至編譯期,可以得到更高的執(zhí)行效率。