模板中class与typename的辨析
目录
引言:
同
在声明类时
在定义模板时
异
模板定义中
依赖类型名称
示例
引言:
在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;}