学习模板元编程(2)std::true_type/false_type
目录
实现原理
应用场景
条件编译
通过特化和继承,实现std::is_xxx系列
思路
举例
例子1,is_bool
例子2,is_ptr
实现原理
std::true_type/false_type是模板intergral_constant的两种实现:
using true_type = integral_constant<bool, true>;
using false_type = integral_constant<bool, false>;
应用场景
条件编译
先看一个不使用true_type/false_type,结果编译失败的例子。
编程者的用意是通过一个布尔型变量,配合一个模板类型参数T,控制类的行为:
#include <iostream>template<class T, bool b>
class fail
{
public:T a;fail(){if (b){a = 10;std::cout << a << std::endl;}else{a = "10";std::cout << a << std::endl;}}
};
int main(void)
{fail<int, true> f;std::cin.get();return 0;
}
编译失败:
诚然,a="10"这个分支是不会在运行时命中的,但是这个分支仍会在编译时被编译。而a是个整型(因为fail<int, true>) ,所以给整型赋值字符串将导致编译失败。
从上面的分析可以看出,运行时仍然保留了两个条件分支,是编译失败的原因。
现在使用std::true_type/false_type,让编译时就命中条件分支,并进行有选择的编译,避免语法错误。
#include <iostream>
//注:这里使用vs2013,而bool_constant是C++17标准引入的,所以这里手动定义
template<bool val>
using bool_constant = std::integral_constant<bool, val>;template<class T, bool b>
class fail
{
public:T a;fail(){
//注:这里一定要有(),
//否则func函数的输入参数就是一个类型bool_constant<b>,而不是一个变量
//而类型不能作为输入参数func(bool_constant<b>());}void func (std::true_type t){a = 10;std::cout << a << std::endl;}void func(std::false_type f){a = "10";std::cout << a << std::endl;}
};
int main(void)
{fail<int, true> f;std::cin.get();return 0;
}
编译通过,且正常运行:
这里采用的是模板匹配的办法,没有直接用true/false命中条件分支,而是先通过true/false实现bool_constant的具体类型(true_type/false_type)。利用类型的区别来匹配合适的func()。
具体实现时,要注意两点:
1 要用func(bool_constant<b>()),而不是func(bool_constant<b>),因为bool_constant<b>只是个类型,不是变量,不能作为输入参数;
2 两个func()函数定义的时候(func(std::true_type), func(std::false_type)),里面还指明了具体的变量名(t,f)。其实也可以不指明变量名,因为这两个变量在函数体内没有用到,也不会因为没有变量名就影响匹配。
通过特化和继承,实现std::is_xxx系列
思路
先假定被判断的类型“不是”定义一个模板,既然不是,就继承false_type,然后再特化之,定义“是”的情况,既然是,就继承true_type。
举例
例子1,is_bool
#include <iostream>template<class T>
struct is_bool : std::false_type
{};template<>
struct is_bool<bool> : std::true_type
{};int main(void)
{std::cout << is_bool<int>() << std::endl;std::cout << is_bool<bool>() << std::endl;std::cin.get();return 0;
}
结果:
至于为什么cout可以直接输出is_bool<int/bool>(),它又不是一个简单的布尔型,而是一个自定义的结构体?那是因为类型转换函数的结果,参见类型转换运算符(conversion operator)-CSDN博客
例子2,is_ptr
代码:
#include <iostream>template<class T>
struct is_ptr : std::false_type
{};template<class T>
struct is_ptr<T *> : std::true_type
{};int main(void)
{std::cout << is_ptr<int>() << std::endl;std::cout << is_ptr<int *>() << std::endl;std::cin.get();return 0;
}
结果:
关于模板如何匹配到指针,那是因为模板匹配的原则是匹配与当前情况最贴切的模板。参看我前面的博客初尝类型萃取--typename、模板偏特化、和traits之(二)模板偏特化-CSDN博客