在模板编程中,有几个常用的技术:模板(偏)特化,特性萃取,标签分派,匹配失败不是错误。其中模板(偏)特化是基础,匹配失败不是错误 (SFINAE)应用最为广泛。
现代 C++ 对模板编程做了更多的加强,boost.hana 又结合 constexpr 和 lambda 把类型与值的计算统一了起来。放眼 C++ 世界,尤其是 C++ 库,几乎都是使用模板的泛型编程。
话说在 C++ 的世界中(并且几乎所有语言中)函数的作用是最为明显的,试想:没有类也可以完成编程任务,但没有函数却不好说。另一方向只使用变量和语句也能完成少量的工作,但不进行函数的封装终难成大事。这几年函数式编程赿来赿流行,与函数的重要性不无关系。所以无论是用类也罢,用函数也罢,或者使用 C++ 新增的变量模板,我们的思路是始终围绕着把它们向函数上靠就好。从 MPL 的元函数开始就试图使类尽可能像函数,我们在模板编程中也要使模板类、模板变量、乃至模板函数都向 "函数" 靠拢。这里函数加上引号我想表达的是包括但不限于 constexpr 函数。
一、先从类型计算将用类实现来转为用函数实现起。将类型计算与值计算统一起来,以例子说话:
- 1 #include
- 2
- 3 //1、先定义一个辅助类,为了将类型表示为对象
- 4 template
- 5 struct type_t
- 6 {
- 7 using type = T;
- 8 };
- 9
- 10 //2、装饰类型,这里给类型添加指针
- 11 template
- 12 constexpr auto add_pointer(type_t)
- 13 {
- 14 return type_t{};
- 15 }
- 16
- 17 //3、为测试提供支持
- 18 template
- 19 constexpr auto is_pointer(type_t)
- 20 {
- 21 return std::false_type{};
- 22 }
- 23
- 24 template
- 25 constexpr auto is_pointer(type_t)
- 26 {
- 27 return std::true_type{};
- 28 }
- 29
- 30 //4、测试
- 31 type_t<int> int_t{};
- 32 auto p = add_pointer(int_t);
- 33
- 34 auto is_ptr = is_pointer(p);
- 35 static_assert(is_ptr.value,"");
- 36
- 37 int main()
- 38 {
- 39 return 0;
- 40 }
我们没有用元函数式方法实现,因为 STL 库中实现好了。现在用函数的方式也可以实现了,仔细体会代码,函数把类型和值的计算统一了起来,看起来函数的作用更加强大了。针对以上例子,我们需要类型的时候可以这样
- 1 // --vs2017rc仍然不能编译--
- 2 //template<typename T>
- 3 //using point_type =typename decltype(add_pointer(type_t<T>{}))::type;
- 4
- 5 using point_type = typename decltype(p)::type;
稍嫌繁琐,但可以进一步封装。
- 1 //将以上代码装到namespace中
- 2 namespace detail {
- 3 //...
- 4 }
- 5
- 6 template
- 7 uisng add_pointer_t = typename decltype(detail::add_pointer(type_t{}))::type;
二、把类函数化。还是以例子说话:
- 1 struct test {};
- 2
- 3 //1、古老的仿函数
- 4 template
- 5 struct func
- 6 {
- 7 template
- 8 auto operator()(Args&&... args) const
- 9 {
- 10 return T{ std::forward(args)... };
- 11 }
- 12 };
- 13
- 14 func func_c;
- 15 auto test_t=func_c();
- 16
- 17 //2、元函数,就不写例子了,STL中很多
func 明明是类,但我们把它用出了函数的感觉有没有。
三、把变量模板函数化
上面的代码感觉用起来还是不爽,主要是因为还要先生成一个类对象,我们把它也封装起来,再看个例子
- 1 struct test2
- 2 {
- 3 explicit test2(int x)
- 4 : x_(x)
- 5 {
- 6
- 7 }
- 8
- 9 int x_;
- 10 };
- 11 template
- 12 constexpr func func_c{};
- 13
- 14 auto test2_t = func_c(1);
看起来就像在调用函数了吧。
总之,不管是什么,只管往函数上靠就对了。
本来想要写 sfinae 来着,却强烈地想函数的事,现在记录一种利用 sfinae 的方式吧。
首先,当然是模板特化了。特化前先说说形式完整性这个概念:
有一个别名声明:
- 1 template
- 3 using void_t=void;
这个别名声明的意思是不管你给我传多少个模板参数,可能是有效的参数我都统统把它们当做 void,除非任意一个模板参数不是有效的,那样的话另说(通常是编译器会给你点颜色看看)。
今天我就是要给你无效参数了怎么地?哼哼,我有 sfinae 大法在手,给我脸色我还不尿你呢!
有了 sfinae,编译器会妥协,不管模板参数有效无效它都不敢给你脸色啦。
嗯,我想想哈,举个标签的例子吧。设想是如果一个类有标签,那么我们获取它的标签,如果没有,就什么也不做。
- 1 templatevoid>
- 2 struct tag_of;
- 3
- 4 template
- 5 struct tag_of>
- 6 {
- 7 using type = typename T::tag;
- 8 };
- 9
- 10 template
- 11 using tag_of_t = typename tag_of::type;
- 12
- 13 template
- 14 struct is_tagof : std::false_type
- 15 {
- 16
- 17 };
- 18
- 19 template
- 20 struct is_tagof> : std::true_type
- 21 {
- 22
- 23 };
模板(偏)特化会优先得到解析。上面 is_tagof 判断类型 T 是否有嵌入的 tag,如果有,is_tagof 就是 true_type, 如果没有的话,编译器在匹配 void_t<typename T::tag> 会失败,编译器不认为这是错误而是回头匹配主模板, is_tagof 就是 false_type
来源: http://www.cnblogs.com/freezestudio/p/6407870.html