C++模板元编程(三)

C++模板元编程(三)

typename和template关键字

  正确的使用关键字typename与template可以很多细微的语法错误,总结如下。

  1. template关键字用来引入模板声明和定义,如:

template <class T> class Vector;

  2. typename关键字通常用来取代class来声明模板类型的参数,如:

template <typename T> class Vector;

  3. typename关键字和class关键字在声明模板类型参数时,一般情况下等价(区别见后面)。但有时选用其中某个关键字,在概念上更加明确,如:

// 模板参数中,第一个是声明一个type参数,第二个是一个类型为T::value_type的值。
// 可见第二种方式,概念更明确,虽然两种模板声明都可以。 
template<typneame T, typename T::value_type> class Test;
template<class T, typename T::value_type> class Test;

  4. 当一个依赖名表示的是一个依赖性的类型时,需要使用typename关键字(对非依赖名,是否使用typename关键字对其进行限定完全是可选的),如:

template<typename Tp>
class IteratorTraits
{
    public:
        // 当使用一个依赖性的类型,且该名字表示一个类型时,C++标准要求使用typename关键字表明该依赖名是一个类型
        typedef typename Tp::value_type value_type;
        typedef typename Tp::reference reference;
        typedef typename Tp::pointer pointer;
        typedef typename Tp::difference_type difference_type;
        typedef typename Tp::iterator_category iterator_category;
};

template<typename T>
class Test
{
    public:
        typename std::vector<T> v; // typename是可选的
    //  typedef typename int value_type; //(错误) typename不能用在非限定性(也就是不带::前缀)上
};

// typedef typename int value; // (错误)typename不能使用在模板之外的任何场合

// typename不能用于基类的名字上,即便它是依赖性的也不行
template<class T> struct Base;

template<class T> struct Derived 
    : typename Base<T>::type // (错误)
{};

// typename不能用于友元声明中
template<class T> class Test
{
    friend class typename T::type;
};

  5. 当一个依赖名是成员模板时,需要使用template消除歧义,如:

template<class T>
int f(T& x)
{
    // x.convert成员函数模板,需要使用template关键字。不然的话可能会将convert当做数据成员,
    // 而解析为(x.convert < 3) > pi, 从而造成歧义
    return x.template convert<3>(Pi)
}

// 注:template禁止用在模板之外的任何场合,包括显示(完全)模板特化。也不能出现在using声明中

注意事项

  1. 模板函数中,参数类型不允许自动类型转换。

  2. 当存在同名的模板函数和非模板函数时,优先调用模板函数。但可以使用空的模板参数列表的形式,从template具现体中挑选适当的对象。

  3. 确保所有形式的重载函数都写在它们的调用点之前。

  4. 以模板类template <typename T> Test{...}申明变量和函数时都应该写成Test,然而只需要class名称而不需要class类型时,
    只需写Test即可。如构造函数、析构函数的什么就属于这种情况。

  5. 在模板类中的模板成员函数,只有被实际调用到了,才会被实例化。

  6. 模板的特化需以template<>开头。

  7. 在模板类的模板参数中,可以指定模板参数的默认值template<typename T, typename U=int>

  8. 模板类的模板参数可以指定为非类型模板参数,同时也可以指定非类型函数模板参数。

  9. 模板的非类型参数只能是整型、enum、外部链接的指针,而不能是浮点类型、类类型对象与内部链接的指针。(在g++ 4.9.1上测试过)

  10. 模板也可以作为模板参数。

  11. 模板的声明与定义要放在同一个文件中。(也可以不放在同一个文件中,使用显示实例化)

  12. 显示实例化,以template打头,模板参数被完全替代。

  13. 成员函数模板不能声明成virtual。

  14. 在同一作用域空间内,类模板不能和其它不同类型的实体共用同一个名称,而非模板类则可以和其它不同实体有相同的名称。

  15. 模板函数不能使用C链接方式(export “C”)。

  16. 模板通常使用外部链接,唯一例外的是static namespace scope 函数模板。

  17. 双重模板参数的声明不能使用struct和union,只能使用class。
    template<template<typename T> class C> ...

  18. 模板的实例化(instantiation)是由泛化的模板定义式产生出实际函数和类型的过程。

  19. Generic Programming, Traits, Policy classes, meta-programming, Expression Template。

  20. Trait表现为一个template parameter的自然附加属性。 Policies表现泛化函数和类型间的可设置行为。

参考资料

  1. 《C++模板元编程》(David Abrahams, Aleksey Gurtovoy )
  2. 《C++ 模板全览》丁志强译
原文地址:https://www.cnblogs.com/corfox/p/5414992.html