Skip to content

Latest commit

 

History

History

Meta-Programming

模板元编程简介

  • 元程序是在编译期间执行的程序,操作的对象也不是普通的变量。因此不能使用c++运行时的关键字(if, else , for) 可用的语法非常有限

  • 最常用的语法

    • enum/static, 用来定义编译期间的整数常量
    • typedef/using, 最重要的元编程关键字,用来定义元数据
    • template, 元编程的“起点”, 主要用于定义元函数
    • “::”, 域作用符, 用于解析类型作用域获取计算结果(元数据)
  • 更高级的用法

    • lambda表达式/容器/迭代器/算法/视图等更高级的概念
  • TypeTraits

    • type_traits提供了一组traits类,也就是元函数,可以在编译时候确定类型(元数据通过using或typedef来使用)是否具有某些traits, 或者为某些类型添加或移除const/volatile等修饰词,提供上百个元函数(就是模板类),主要有以下两大类
      • 检查元数据属性的值的元函数: 以::value返回一个bool值或一个证书
      • 操作元数据的标准元函数: 以::type返回一个新的元数据(类型)
    • 以is或has开头的元函数都属于值元函数,其他属于标准元函数,一些有例外
  • type_traits 库提供四个元函数计算元数据之间的关系,它们都是两参数元函数,使用::value返回bool类型的检查参数

  • Meta-Data Computations(1)

    • add_const 返回T const
    • add_lvalue_reference 对于对象或者函数类型返回左值引用,通常是T&,否则是T
    • add_rvalue_reference 对于对象或者函数类型返回右值引用,通常是T&&, 否则是T
    • remove_cv 移除T的顶层const和volatile修饰
  • Meta-Data Computations(2)

    • make_signed
    • make_unsigned
    • integral_promotion
    • promote
    • remove_extent 移除数组的最顶层维度
    • remove_bounds 同上,但非C++11标准
    • remove_all_extents 变为普通类型
  • Meta-Data Computations(3)

    • conditional<b, T, U> 条件运算,类似于mpl::if_c<> 根据b的真假决定返回T或U
    • common_type<T, ...> : 求多个类型的共通类型(类似于数字的最小共倍数)对类型的顺序由要求
    • decay : 先执行remove_reference得到元数据U, 如果U为数组类型, 返回remove_extent, 若为函数类型,则结果为U, 否则返回U。通常来说,decay得到一个值类型
  • Function Meta Data

    • 解析元数据的元函数function_traits<>不属于C++11标准,是一个非标准元函数,能返回多个值, 包括函数的参数数量/参数类型/返回类型,支持解析最多10个参数的函数
    • function_traits要求输入的元数据(tpye),必须要满足is_function::value==true,不能是 函数指针或者引用。如果是函数指针或引用,那么可以用remove_pointer,remove_reference来转换 类型,否则会导致编译错误,:blush:
    • 摘要如下:
    template <class T>
    sturct function_traits
    {
        static const std::size_t arity;  //arity意思是参数数量
        typedef some-define result_type; //函数的返回类型
        typedef some-define argN_type;   //返回函数第N个参数的类型
    }
    • boost::result_of<>::type可以处理任意可调用类型,函数/函数指针和函数对象

TypeTraits使用的例子

Boost的type_traits实现原理

  • 实现比较复杂,而且使用了预处理元编程和一些特别的技巧
  • 下面针对is_integral<>为例子简单阐述实现原理 - type_traits库里面许多值元函数都使用了元函数转发技术,把元参数转发给元函数integral_constant<>进行计算 (这个又把元参数转发给了元函数mpl::integral_c<>)进行计算,也就是说integral_constant>是大多数值元函数 的public基类。 - 类摘要:
```cpp
template <class T, T val>    //计算类型为T,值为val的整数
struct integral_constant
{
    typedef integral_constant<T, val> type;  //定义自身为返回元数据
    typedef T value_type;     //返回值的类型
    static const T value = val;       //以::value 返回整数值 val
    // BOOST_STATIC_CONSTANT(T, value=val)  //Could be Replaced With this Macro
}
```   
  • 因为type_traits中大部分元函数的计算结果是Bool值,因此type_traits库又特别提供了两个针对Bool元数据 特化的无参元函数true_type和false_type
typedef integral_constant<bool, true> true_type;
typedef integral_constant<bool, false> false_type;
  • is_integral<> 使用了模板特化技术,对于非整数的类型元函数
template<typename T>
struct is_integral: boost::integral_constant<bool, false> //元函数转发,返回false
{};

template<>
struct is_integral<bool>: boost::integral_constant<bool,true>  //元函数转发,返回true

template<>
struct is_integral<char> : booost::integral_constant<bool, true>

例子

模板元编程应用例子

  • Conditional 实现MetaFuctionTool中功能

    • 总结:使用type_traits元函数实现明显比直接的模板特化复杂,充分展现了模板元编程的函数式本质, 程序的实现都是通过函数的嵌套调用完成的,程序员需要在头脑中维护一个“函数的堆栈”才能搞清楚它们的调用过程。
    • 实际上conditional<>并没有左什么更多的工作,它知识对mpl::if_c的元函数转发
    template<bool b, class T, class U>
    struct conditional: public mpl::if_c<b, T, U>  //元函数转发
    {};
    • boost.mpl里另有一个元函数eval_if<>也可以达到相同效果
  • Identity Type

    • 问题:C/C++预处理器会把逗号识别成宏参数分隔符,不能理解C++模板语法的尖括号,所以在宏里使用带有逗号的 模板类会导致参数解析错误
  • declval

    • C++11标准中一个特别的函数模板
    template <typename T>
    typename add_rvalue_reference<T>::type declval();
    • Boost中在boost/utility/decval.hpp中实现了它
    • type_traits里面的common_type<>使用了decval()实现,计算两个类型的代码:
    template<typename T, typename U>
    struct common_type<T, U>{
        typedef decltype(decval<bool> ? decval<T>() : decval<U>()) type;
    }
    • 对于更多拉稀行的共通计算,common_type<>使用了递归
    template<typename T, typename U, typename... V>   //C++11可变参数列表
    struct common_type<T, U, V...>
    public:
        typedef typename common_type<       //递归调用common_type
            typename common_type<T,U>::type, V...>::type type;

总结

  • 模板元编程基本概念:元数据(类型),元函数(模板类),元函数转发(通过继承,改变参数位置等应用)。
  • 主要用途:类型推导
  • 元数据:可以是整数(含bool)或任意c++类型
  • 元函数:元编程的核心,内部定义::type或者::value返回计算的结果
  • 元函数转发:通过使用public继承元函数的方式实现
  • 其他既有的库
    • std::result_of 能够推导出调用类型(函数,函数指针,函数对象)的返回值
    • std::unwrap_reference 揭开boost::reference_wrapper的包装
    • boost::call_traits:推导出最适合的调用参数类型
  • Traits是一个重要概念,萃取类型中重要的信息,如iterator_traits和function_traits