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<typename T>
 
void print2nd(const T& container)
 
{
 
    //...
 
    //iterator可能被编译器理解为C的static成员变量,x为一个变量,下面是两个变量的相乘
 
    T::iterator* x;
    
    //...

}
 

这段代码的意义:定义一个指针x,它指向的类型是包含类作用域T中的iterator。

像前面所说的,T::iterator可能是静态数据成员,如下所示:

struct ContainsAnotherType {
    static int iterator;
    // ...

};

此时 T::iterator* x被编译器实例化为ContainsAnotherType::iterator * iter,此时变成了一个乘法表达式,编译器会报错。

4. typename

4.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是一个类型,不是一个变量,而不需要等到实例化时确定
    
    //...

}
 

4.2 使用规则

以下情况禁止使用typename:

1)模板定义之外,即typename只能用于模板的定义中

2)非限定类型,比如前面介绍过的intvector<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
 
    }
 

4.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的别名

5 总结

Effective C++条款42:模板与泛型编程(了解typename的双重意义)中有如下总结:

1)声明template参数时,前缀关键字class和typename可互换

2)请使用关键字typename标识嵌套从属类型名称;但不得在base class lists(基类列)或member initialization list(成员初值列)内以它作为base class修饰符

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值