本文主要是介绍111 C++ typename,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
介绍typename之前,应先了解几个概念:
1. 限定名与非限定名
限定名,顾名思义,是限定了命名空间的名称。
#include <iostream>int main()
{std::cout << "Hello world!" << std::endl;
}
std::限定了std这个命名空间,故称为限定名。
#include <iostream>
using namespace std;int main()
{cout << "Hello world!" << endl;
}
使用了using namespace std,就不再需要std::限定,此时cout和endl叫做非限定名。
2. 依赖名与非依赖名
依赖名是指依赖于模板参数的名称,
非依赖名指不依赖模板参数的名称。
template <class T>
class MyClass {int i;vector<int> vi;vector<int>::iterator vitr;T t; //由于依赖于模板参数T,只有在模板实例化的时候才能知道他们的类型vector<T> vt;vector<T>::iterator viter;
};
T、vector<T>和vector<T>::iterator称为依赖名,
int、vector<int>和vector<int>::iterator称为非依赖名。
3. 类作用域
类外部访问类中的名称时,存在三种方式:
1)静态数据成员
2)静态成员函数
3)嵌套类型
struct MyClass {
static int A;
static int B();
typedef int C;
}
可分别使用MyClass::A、MyClass::B和MyClass::C表示
4. 引入typename的原因
来看下面一个例子:
template<class T>
void foo()
{T::iterator * iter;
}
T::iterator 这样的一个定义可以是以上三种类作用域中的任意一种类型。
(1)如果iterator是嵌套类型将正确执行,这段代码的意思是定义一个 T::iterator类型的数据。
(2)当iterator是静态数据成员时,以上代码将被解释为两个数相乘,返回值抛弃。如果iter没有定义,将报错;但如果iter是全局变量,将执行。
在模板实例化之前完全没有办法区分,因此,有必要引入新的关键字typename来区分这两种情况。
5. typename
5.1 基本使用
c++标准中有这么一句话:
A name used in a template declaration or definition and that is dependent on a template-parameter is assumed not to name a type unless the applicable name lookup finds a type name or the name is qualified by the keyword typename.
对于用于模板定义的依赖于模板参数的名称,只有在实例化的参数中存在这个类型名,或者这个名称前使用了typename关键字来修饰,编译器才会将该名称当成是类型。除了以上这两种情况,绝不会被当成是类型。
上面的例子可以修改为:
template<typename T>void print2nd(const T& container){typename T::iterator* x; //让编译器确定T::iterator是一个类型,不是一个变量,而不需要等到实例化时确定//...}
5.2 使用规则
以下情况禁止使用typename:
1)模板定义之外,即typename只能用于模板的定义中
2)非限定类型,比如前面介绍过的int,vector<int>之类
3)基类列表中,比如class Derived :public Base<T>::Nested不能在public Base<T>::Nested前面加typename
4)构造函数的初始化列表中
template<typename T>class Derived :public Base<T>::Nested //此处不可以使用typename{public:explicit Derived(int):Base<T>::Nested(x)//此处不可以使用typename{typename Base<T>::Nested temp; //此处可以使用typename}
5.3 traits中的使用
代码示例:
template<typename IterT>
void workWithIterator(IterT iter)
{
typename std::iterator_traits<IterT>::value_type temp(*iter);
//...
}
此处我们使用到了iterator_traits<>模板类,其实是一种traits类。我们传递给其一个迭代器类型为其进行实例化,那么我们就可以通过其value_type萃取出迭代器所指的容器的类型。例如:
1)如果IterT是list<string>::iterator,那么value_type就代表string,temp的类型就是string
2)如果IterT是vector<int>::iterator,那么value_type就代表int,temp的类型就是int
因为value_type也是一种内嵌类型,因此我们需要使用typename声明其是一种类型
如果上面的代码比较复杂,那么我们还可以搭配typedef来使用,typedef是声明一个类型的别名。
template<typename IterT>
void workWithIterator(IterT iter)
{
typedef typename std::iterator_traits<IterT>::value_type value_type; //为类型声明别名
value_type temp(*iter); //使用类型定义变量
//...
}
stl源码中有很多类似的例子,例如:
typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
它表示:将__type_traits<T>这个模板类中的has_trivial_destructor嵌套类型定义一个叫做trivial_destructor的别名
6 总结
Effective C++条款42:模板与泛型编程(了解typename的双重意义)中有如下总结:
1)声明template参数时,前缀关键字class和typename可互换
2)请使用关键字typename标识嵌套从属类型名称;但不得在base class lists(基类列)或member initialization list(成员初值列)内以它作为base class修饰符
这篇关于111 C++ typename的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!