一、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》
欢迎大家评论交流,作者水平有限,如有错误,欢迎指出