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都可以

C++知识点61——typename与class、模板编程与继承、模板类和友元、类模板与static成员

但是,第11行只能用typename,不能用class,否则编译器报错

C++知识点61——typename与class、模板编程与继承、模板类和友元、类模板与static成员

编译器提示,和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;
	}
};

上述代码无法编译通过

C++知识点61——typename与class、模板编程与继承、模板类和友元、类模板与static成员

编译时,编译器提示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》

 

欢迎大家评论交流,作者水平有限,如有错误,欢迎指出

上一篇:ASP.NET Ajax调用WCF服务示例


下一篇:泛微OA之查找系统上共有多少流程