目录
引言:
在C++编程语言中,typename
和class
这两个关键字在许多情况下是可以互换使用的,但它们也有一些特定的用途和含义上的差异。那什么时候该用class,什么时候该用typename呢?
同
在声明类时
class MyClass {
// ...
};
typename MyClass2 {
// ...
};
尽管技术上允许使用typename来声明类,但在实际编码实践中,几乎总是使用class来声明类,因为这是C++的传统用法,并且更符合语言的习惯。
在定义模板时
在定义模板时,都可以用来声明模板参数作为类型。以下typename与class都可以。
template<typename T>
void function(T param) {
// ...
}
typename告诉编译器T是一个类型参数。在C++98标准之前,这是必须的,以区别于非类型模板参数。然而,在C++11及以后的版本中,你可以在模板参数列表中使用class来代替typename。
异
在有些情况中,只能使用typename,而不能使用class。以下是需要使用typename
替换class
的情况
模板定义中
当在模板定义内部使用一个依赖于模板参数的类型时,通常需要在该类型前面加上typename关键字,以指示它是一个类型名,而不是静态成员变量或者其它非类型实体。
template <typename T>
class Example {
public:
typename T::SubType* ptr; // 这里需要typename,因为SubType依赖于模板参数T
};
(当我们使用静态成员变量时,不需要在前面加上typename关键字,因为静态成员变量不是类型。相反,应该直接使用类名和作用域解析运算符::来访问静态成员变量)
依赖类型名称
在一个模板内部,如果你引用一个依赖于模板参数的类型名称(即嵌套依赖类型),你需要使用typename来明确指出这是一个类型。
template <typename T>
void function() {
typename T::NestedType var; // typename用于指示NestedType是T的一个类型
}
示例
例1:
在下述代码中,我们想要完成list<int>类型的打印工作。
void print_list(const list<int>& lt)
{
list<int>::const_iterator it = lt.begin();
while (it != lt.end())
{
//*it = 10;
cout << *it << " ";
++it;
}
cout << endl;
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
}
list<int>::const_iterator it = lt.begin();对于这句代码,list<int>已经点名了数据类型是什么,不需要用typename。
例2:
当我们想要同时满足打印其他类型时(list<string>),这时候就无法完成任务,因此我们可以借助模板函数完成。
template<typename t>
//template<class t>
void print_list(const list<t>& lt)
{
typename list<t>::const_iterator it = lt.begin();
while (it != lt.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}
list<t>未实例化的类模板,编译器不能去他里面去找编译器就无法分别list<t>::const_iterator是内嵌类型,还是静态成员变量前面加一个typename就是告诉编译器,这里是一个类型,等list<t>实例化再去类里面去取。
当然,成功实现该功能还得益于,string类型完成了流插入的重载。
例3:
当我们想要再满足可以打印其他容器的内容时(vector(string)),这时候就在需要进行一次修改。
将模板参数t修改为容器参数。
typename Container::const_iterator it = con.begin();
template<typename Container>
void print_container(const Container& con)
{
typename Container::const_iterator it = con.begin();
while (it != con.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}