
先前部分的摘要
由于使用C ++ 11编译器的能力受到限制,并且由于缺乏替代性,boost希望在编译器随附的C ++ 98 / C ++ 03库之上编写自己的标准C ++ 11库实现。
实现了static_assert ,
noexcept ,
countof ,并且在考虑了所有非标准定义和编译器功能之后,出现了有关当前编译器支持的功能的信息。
包含了自己的
nullptr实现,该实现在编译阶段选择。
现在是使用
type_traits和所有这些“特殊模板魔术”的时候了。
链接到GitHub,为不耐烦的读者和非读者提供今天的结果:
欢迎有建设性的批评和批评
将自己沉浸在“模板魔术” C ++的世界中。
目录
引言第1章。Viam supervadet vadens第2章。#ifndef __CPP11_SUPPORT__#定义__COMPILER_SPECIFIC_BUILT_IN_AND_MACRO_HELL__ #endif第3章。找到理想的nullptr实现第4章C ++模板魔术....
4.1我们从小处着手....
4.2关于日志为我们编译了多少个奇迹般的错误....
4.3指针和所有所有....
4.4模板库还需要什么第五章
...
第4章C ++模板魔术
完成了C ++ 11关键字以及它们的实现之间的所有依赖定义的“开关”之后,我开始填写
type_traits 。 实际上,我已经有很多类似于标准模板类的模板类,它们已经在项目中使用了很长时间,因此仍然需要将所有这些都以相同的形式呈现,并添加缺少的功能。

老实说,我受到模板编程的启发。 尤其是意识到所有这些都是多种选择的:在编译过程中执行计算,代码分支,条件,错误检查,而在执行阶段则不花任何代价最终程序。 并且由于C ++中的模板本质上是一种
图灵完备的编程语言 ,所以我期待着实现与模板编程相关的标准部分的优雅和相对容易。 但是,为了立即消除所有幻想,我将说整个图灵完整性理论被分解为编译器中模板的具体实现。 而编写库的这一部分,而不是优雅的解决方案和模板编程的“技巧”,变成了与编译器的激烈斗争,而每个人都以自己的方式“崩溃”了,如果它陷入了严重的内部编译器错误,或者甚至崩溃了,那就很好了。未处理的异常。 GCC(g ++)表现出了自己最好的一面,它从逻辑上“咀嚼”了所有模板的构造,并且仅在缺少显式
typename的地方诅咒(在这种情况下)。
4.1从小做起
我从简单的模板开始,分别是
std :: integral_constant ,
std :: bool_constant和类似的小模板。
template<class _Tp, _Tp Val> struct integral_constant {
基于
条件,您可以为类型上的逻辑操作{“ and”,“ or”,“ not”}输入方便的模板(所有这些操作都在编译阶段就被认为是正确的!这很好,不是吗?):
namespace detail { struct void_type {};
这里有三点值得关注:
1)务必在模板的尖括号('<'和'>')之间放置一个空格,这是很重要的,因为在C ++ 11之前,标准中尚无关于如何解释_or _ <_ B2等代码中的'>>'和'<<'的说明, _或_ <_ B3,_B4 >> ,因此几乎所有编译器都将其视为移位运算符,这会导致编译错误。
2)在某些编译器(例如Visual Studio 6.0)中,存在一个错误,该错误在于无法将void类型用作模板参数。 为此,在上面的段落中引入了一个单独的void_type类型,以替换需要默认模板参数值的void类型。
3)非常老的编译器(例如,Borland C ++ Builder)具有弯曲的实现类型bool ,在某些情况下,该类型突然“转变”为int ( true- > 1, false- > 0),以及该类型的常量静态变量类型bool (不仅限于它们),如果它们包含在模板类中。 由于所有这些混乱,因此,对于my_template_type :: static_bool_value == false样式的完全无害的比较,编译器可以轻松地发出无法将'undefined type' 强制转换 为int(0)之类的附魔 。 因此,有必要始终尝试明确指出要比较的值的类型,从而帮助编译器确定其处理的类型。
使用
const和
volatile值添加更多工作。 首先,简单地实现
remove_ ...,在这里我们只是简单地对某些类型修饰符进行模板专用化-如果类型带有修饰符,则编译器必须在查看了模板的所有特定性(从
上一章中回顾了SFINAE原理)后,选择最合适的(明确指出了所需的修饰符) :
template<class _Tp> struct is_function; template<class _Tp> struct remove_const {
然后我们实现
add_模板...一切都已经有些复杂了:
namespace detail { template<class _Tp, bool _IsFunction> struct _add_const_helper { typedef _Tp const type; }; template<class _Tp> struct _add_const_helper<_Tp, true> { typedef _Tp type; }; template<class _Tp, bool _IsFunction> struct _add_volatile_helper { typedef _Tp volatile type; }; template<class _Tp> struct _add_volatile_helper<_Tp, true> { typedef _Tp type; }; template<class _Tp, bool _IsFunction> struct _add_cv_helper { typedef _Tp const volatile type; }; template<class _Tp> struct _add_cv_helper<_Tp, true> { typedef _Tp type; }; }
在这里,我们仔细地分别处理引用类型,以免丢失链接。 同样,我们不会忘记原则上不可能使
volatile或
const成为可能的函数类型,因此我们将它们保持“原样”。 我可以说所有这些看起来非常简单,但是当“魔鬼在细节中”,或者“错误在实现的细节中”时,情况就是这样。
第四章第一部分结束。 在
第二部分中,我将讨论如何对编译器进行硬模板编程,并且还会有更多很棒的模板魔术。 嗯,但是-根据当今的一些编译器
,为什么
长久以来都不是
整数常数 。
谢谢您的关注。