zhouqijie

元函数于type_traits

元函数简介

C++的元编程是一种函数式编程,这里的函数更接近数学意义上的函数–是无副作用的映射。如果函数存在副作用,那么通常是由于存在某些维护了系统状态的变量导致的,这样的函数称为有副作用的函数。

元函数会在编译期被调用和执行。在编译阶段,编译器只能构造常量作为其中间结果,无法构造并维护记录系统状态的变量,因此编译器可以使用的函数(即元函数)只能是无副作用的函数。

//定义一个函数,满足无副作用的限制,可以作为元函数使用。    
constexpr int func(int a){ return a + 1;}

其中的constexpr为C++11的关键字,表明这个函数可以在编译期被调用,是一个元函数。如果去掉这个关键字,那么函数将只能用于运行期,虽然它具有无副作用的性质,但也无法编译期调用。如果有constexpr关键字但是不满足“无副作用”的性质,那么将无法通过编译。

关键字const是一种类型修饰符,用于表示变量的值在初始化后不能被改变。const在运行时初始化,一旦初始化就不能改变。在某些情况下(全局、局部静态变量)编译器可能将const变量视为编译时常量,但是依赖上下文。

CRE:也就是const可以是运行时常量也可以是编译期常量。但是constexpr限定为编译期常量。
知乎:这其实是一个遗留的命名问题,const其实是readonly,constexpr才是const。

类型元函数

元编程的核心是元函数,元函数的输入、输出形式可以有很多种,数值只是其中一种,也可以将类型作为元函数的输入或输出。

//元函数定义  
template<typename T>
struct _Func{ using type = T; };
template<>
struct _Func<int>{ using type = unsigned int; };
template<>
struct _Func<long>{ using type = unsigned long; };

//元函数调用  
_Func<int>::type h = 3;

Func与C++一般意义的函数完全不同,但是Func具备了一个元函数所需要的全部性质:

  1. 输入为某个类型T。(以模板参数形式传递到Func模板中)
  2. 输出为Func模板的内部类型type。
  3. 映射体现为模板通过特化实现类型转换逻辑。

元函数不限制映射的表示形式,可以是constexpr函数、也可以是内嵌type的模板,乃至其他形式的“函数”,只要无副作用,同时可以编译时期调用,都视作元函数。
元函数不限制输入和输出的形式,可以是数值、类型甚至模板。

//另一种元函数,用于简化模板的使用    
template<typename T>
using Func = typename _Func<T>::type;
//调用  
Func<int> h = 3;

各式各样的元函数

只要确保所构造的映射是“无副作用”的,可以在编译期调用,用于对编译器乃至运行期的程序行为产生影响,那么相应的映射都可以称为元函数。

CRE:元函数不一定是个C++函数。

//无参元函数  
struct Func { using type = int; }
constexpr int func() { return 10; }
//基于C++14的constexpr的扩展的元函数    
template<int a>
constexpr int fun = a + 1;
//多返回值  
template<>
struct _Func<int>
{
    using reference_type = int&;
    using const_reference_type = const int&;
    using value_type = int;
}

type_traits

一个重要的元函数库:type_traits。它是由boost引入的,C++11纳入其中。通过头文件type_traits来引入相应功能。这个库实现了类型变换、类型比较与判断等。

std::remove_reference<int&>::type  h1 = 3;
std::remove_reference_t<int&>  h2 = 3;

元函数与宏

理论上宏也可以被视为一类元函数。但讨论元函数时一般不包括宏,因为宏是由预处理器而非编译器解析的,很多编译期特性宏是无法使用的。

参考

《C++模板元编程实战》 – 李伟

(END)