1.概述
模版元编程(template metaprogram)是C++中最复杂也是威力最强大的编程范式,它是一种可以创建和操纵程序的程序。模版元编程完全不同于普通的运行期程序,它很独特,因为模版元程序的执行完全是在编译期,并且模版元程序操纵的数据不能是运行时变量,只能是编译期常量,不可修改,另外它用到的语法元素也是相当有限,不能使用运行期的一些语法,比如if-else,for等语句都不能用。因此,模版元编程需要很多技巧,常常需要类型重定义、枚举常量、继承、模板偏特化等方法来配合,因此编写模版元编程比较复杂也比较困难。
为了帮助您更好地入门并深入掌握C++,我们精心准备了一系列丰富的学习资源包,包括但不限于基础语法教程、实战项目案例、核心概念解析以及进阶技巧指导等。
您只扫码上方二维码,即可免费获取这份专属的学习礼包。我们的教程覆盖了C++语言的各个方面,旨在让您在理论学习与实践操作中不断进步,提升编程技能。
同时,我们也鼓励您在学习过程中遇到任何问题时积极提问,我们会尽全力提供解答和帮助。期待您在C++编程的道路上越走越远,早日成为一位优秀的C++开发
现在C++11新增了一些模版元相关的特性,不仅可以让我们编写模版元程序变得更容易,还进一步增强了泛型编程的能力,比如type_traits让我们不必再重复发明轮子了,给我们提供了大量便利的元函数,还提供了可变模板参数和tuple,让模版元编程“如虎添翼”。本文将向读者展示C++11中模版元编程常用的技巧和具体应用。
2.模版元基本概念
模版元程序由元数据和元函数组成,元数据就是元编程可以操作的数据,即C++编译器在编译期可以操作的数据。元数据不是运行期变量,只能是编译期常量,不能修改,常见的元数据有enum枚举常量、静态常量、基本类型和自定义类型等。
元函数是模板元编程中用于操作处理元数据的“构件”,可以在编译期被“调用”,因为它的功能和形式和运行时的函数类似,而被称为元函数,它是元编程中最重要的构件。元函数实际上表现为C++的一个类、模板类或模板函数,它的通常形式如下:
template<int N, int M>
struct meta_func
{
static const int value = N+M;
}
调用元函数获取value值:cout<<meta_func<1, 2>::value<<endl;
meta_func的执行过程是在编译期完成的,实际执行程序时,是没有计算动作而是直接使用编译期的计算结果的。元函数只处理元数据,元数据是编译期常量和类型,所以下面的代码是编译不过的:
int i = 1, j = 2;
meta_func<i, j>::value; //错误,元函数无法处理运行时普通数据
模板元编程产生的源程序是在编译期执行的程序,因此它首先要遵循C++和模板的语法,但是它操作的对象不是运行时普通的变量,因此不能使用运行时的C++关键字(如if、else、for),可用的语法元素相当有限,最常用的是:
- enum、static const,用来定义编译期的整数常量;
- typedef/using,用于定义元数据;
- T、Args...,声明元数据类型;
- template,主要用于定义元函数;
- "::",域运算符,用于解析类型作用域获取计算结果(元数据)。
如果模板元编程中需要if-else、for等逻辑时该怎么办呢?
模板元中的if-else可以通过type_traits来实现,它不仅仅可以在编译期做判断,还可以做计算、查询、转换和选择。
模板元中的for等逻辑可以通过递归、重载、和模板特化(偏特化)等方法实现。
下面来看看C++11提供的模版元基础库type_traits。
3.type_traits
type_traits是C++11提供的模板元基础库,通过type_traits可以实现在编译期计算、查询、判断、转换和选择,提供了模板元编程需要的一些常用元函数。下面来看看一些基本的type_traits的基本用法。
最简单的一个type_traits是定义编译期常量的元函数integral_constant,它的定义如下:
template< class T, T v >
struct integral_constant;
借助这个简单的trait,我们可以很方便地定义编译期常量,比如定义一个值为1的int常量可以这样定义:
using one_type = std::integral_constant<int, 1>;
或者
template<class T>
struct one_type : std::integral_constant<int, 1>{};
获取常量则通过one_type::value来获取,这种定义编译期常量的方式相比C++98/03要简单,在C++98/03中定义编译期常量一般是这样定义的:
template<class T>
struct one_type
{
enum{value = 1};
};
template<class T>
struct one_type
{
static const int value = 1;
};
可以看到,通过C++11的type_traits提供的一个简单的integral_constant就可以很方便的定义编译期常量,而无需再去通过定义enum和static const变量方式去定义编译期常量了,这也为定义编译期常量提供了另外一种方法。C++11的type_traits已经提供了编译期的true和false,是通过integral_constant来定义的:
typedef integral_constant<bool, true> true_type;
typedef integral_constant<bool, false> false_type;
除了这些基本的元函数之外,type_traits还提供了丰富的元函数,比如用于编译期判断的元函数:
这只是列举一小部分的type_traits元函数,type_traits提供了上百个方便的元函数,读者可以参考http://en.cppreference.com/w/cpp/header/type_traits,这些基本的元函数用法比较简单:
#include <iostream>
#include <type_traits>
int main() {
std::cout << "int: " << std::is_const<int>::value << std::endl;
std::cout << "const int: " << std::is_const<const int>::value << std::endl;
//判断类型是否相同
std::cout<< std::is_same<int, int>::value<<"\n";// true
std::cout<< std::is_same<int, unsignedint>::value<<"\n";// false
//添加、移除const
cout << std::is_same<const int, add_const<int>::type>::value << endl;
cout << std::is_same<int, remove_const<const int>::type>::value << endl;
//添加引用
cout << std::is_same<int&, add_lvalue_reference<int>::type>::value << endl;
cout << std::is_same<int&&, add_rvalue_reference<int>::type>::value << endl;
//取公共类型
typedef std::common_type<unsigned char, short, int>::type NumericType;
cout << std::is_same<int, NumericType>::value << endl;
return 0;
}
type_tra