元函数于type_traits
元函数简介
C++的元编程是一种函数式编程,这里的函数更接近数学意义上的函数–是无副作用的映射。如果函数存在副作用,那么通常是由于存在某些维护了系统状态的变量导致的,这样的函数称为有副作用的函数。
元函数会在编译期被调用和执行。在编译阶段,编译器只能构造常量作为其中间结果,无法构造并维护记录系统状态的变量,因此编译器可以使用的函数(即元函数)只能是无副作用的函数。
//定义一个函数,满足无副作用的限制,可以作为元函数使用。
constexpr int func(int a){ return a + 1;}
const
和constexpr
的关系
其中的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具备了一个元函数所需要的全部性质:
- 输入为某个类型T。(以模板参数形式传递到Func模板中)
- 输出为Func模板的内部类型type。
- 映射体现为模板通过特化实现类型转换逻辑。
元函数不限制映射的表示形式,可以是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)