C++知識點61——typename與class、模板程式設計與繼承、模板類和友元、類模板與static成員
一、typename與class的異同
1、啥時候既可以使用typename,又可以使用class?
當表示模板引數的時候,二者沒有區別
2、啥時候只能使用typename,不能使用class?
當模板內部的成員表示一個型別時,必須使用typename,而不能使用class
雖然書上是這樣寫的,但是實際並不使這樣,在g++ 7.5.0測試發現,即使表示一個模板引數的型別成員,typename和class也沒有明顯差異
示例1
template <class T> class mytest { class T::val_type func(const T &c); }; template <class T> class T::val_type mytest<T>::func(const T &c) { typename T::val_type *ptr; class T::val_type t; return typename T::val_type(); }
上述程式碼中的第10行必須加typename或者class關鍵字,否則,編譯器會認為型別T中的static成員val_type與ptr相乘,而ptr沒有定義,然而使用typename或者class都可以
但是,第11行只能用typename,不能用class,否則編譯器報錯
編譯器提示,和class一起使用的表示式不正確,所以必須typename的原因是class後面的表示式不正確,而不是什麼只能用typename不能用class
示例2
template <typename T> void print(const T &con) { class T::const_iterator pos; class T::const_iterator end(con.end()); for (pos=con.begin();pos!=end;++pos) { cout<<*pos<<endl; } }
上述程式碼負責列印容器中的元素,第4,5行程式碼雖然都是表示T中的迭代器型別,但是使用class和typename都可以
所以,使用模板引數中的型別成員時,用class還是typename沒有區別,只有class後的表示式不正確時,才必須使用typename
所以,既然能使用class的地方都能使用typename,但是有的地方還不能使用class,所以,在模板程式設計中,只使用typename表示型別就對了
二、模板程式設計與繼承
如果一個類模板被當做基類,如果子類不是模板類,那麼派生列表要指定基類的模板實參,如果子類也是個類模板,那麼,子類的派生列表可以使用自身的模板引數來指定基類的模板實參
示例
template <typename T>
class base
{
};
template <typename T>
class derive:public base<T>
{
};
class derive2:public base<int>
{
};
如果子類是個類模板,並且派生列表中的基類使用了子類的模板引數,那麼在子類成員函式中呼叫基類成員函式時,要指定作用域
template <typename T>
class base
{
public:
void functest(){cout<<__func__<<"in base"<<endl;}
};
template <typename T>
class derive:public base<T>
{
public:
void func(){
functest();
cout<<__func__<<"in derive"<<endl;
}
};
上述程式碼無法編譯通過
編譯時,編譯器提示functest是個依賴模板引數的函式,但是沒有指明模板引數,所以編譯器報錯。所以解決辦法有兩個:
1、加上基類的作用域
emplate <typename T>
class derive:public base<T>
{
public:
void func(){
base<T>::functest();
cout<<__func__<<"in derive"<<endl;
}
};
2、在子類中重新定義一個functest
template <typename T>
class derive:public base<T>
{
public:
void func(){
functest();
cout<<__func__<<"in derive"<<endl;
}
void functest(){cout<<__func__<<"in derive"<<endl;}
};
如果子類只是個普通類,但是要類模板做基類,那麼必須將基類模板例項化
class derive2:public base<int>
{
public:
void func(){
functest();
cout<<__func__<<"in derive"<<endl;
}
};
此時編譯器並不會報錯,因為基類是個具體的類,建立該子類物件時,基類的this已經確定,可以找到基類的的functest
三、模板類和友元
如果一個類模板中包含一個友元,如果友元不是模板,那麼,友元可以訪問所有模板的例項,無須解釋,和以前一樣
如果友元是模板,那麼友元模板的所有或者部分例項可以訪問所有的類模板的例項
示例
template <typename T>
class friend1
{
};
template <typename T>
class friend2
{
};
template <typename T1>
class test2
{
template <typename T2>
friend class friend1;//類模板friend1的所有例項都是test2例項的友元
friend class friend2<T1>;//只有用T1例項化的類才是test2的友元
};
四、類模板與static成員
類模板的每個例項類都各自擁有類模板中的static成員
示例
template <typename T>
class statictest
{
static void func() {}
static int si;
};
template <typename T>
int statictest<T>::si=0;
int main(int argc, char const *argv[])
{
statictest<int> t;
int i=t.si;
//int i2=statictest::si;
}
因為static成員是各個例項化的模板類各自所有,而上述程式碼中的第15行沒有指定模板引數,需要註釋掉
static成員函式和其他類模板的成員函式一樣,只有在被呼叫時才會被例項化產生程式碼
參考
《C++ Template》
《C++ Primer》
歡迎大家評論交流,作者水平有限,如有錯誤,歡迎指出