我如何编写标准的C ++ 11库,或者为什么boost如此令人恐惧。 第4.1章

我们继续冒险。

先前部分的摘要


由于使用C ++ 11编译器的能力受到限制,并且由于缺乏替代性,boost希望在编译器随附的C ++ 98 / C ++ 03库之上编写自己的标准C ++ 11库实现。

实现了static_assertnoexceptcountof ,并且在考虑了所有非标准定义和编译器功能之后,出现了有关当前编译器支持的功能的信息。 包含了自己的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_constantstd :: bool_constant和类似的小模板。

template<class _Tp, _Tp Val> struct integral_constant { // convenient template for integral constant types static const _Tp value = Val; typedef const _Tp value_type; typedef integral_constant<_Tp, Val> type; operator value_type() const { // return stored value return (value); } value_type operator()() const { // return stored value return (value); } }; typedef integral_constant<bool, true> true_type; typedef integral_constant<bool, false> false_type; template<bool Val> struct bool_constant : public integral_constant<bool, Val> {}; // Primary template. // Define a member typedef @c type to one of two argument types. template<bool _Cond, class _Iftrue, class _Iffalse> struct conditional { typedef _Iftrue type; }; // Partial specialization for false. template<class _Iftrue, class _Iffalse> struct conditional<false, _Iftrue, _Iffalse> { typedef _Iffalse type; }; 

基于条件,您可以为类型上的逻辑操作{“ and”,“ or”,“ not”}输入方便的模板(所有这些操作都在编译阶段就被认为是正确的!这很好,不是吗?):

 namespace detail { struct void_type {}; //typedef void void_type; template<class _B1 = void_type, class _B2 = void_type, class _B3 = void_type, class _B4 = void_type> struct _or_ : public conditional<_B1::value, _B1, _or_<_B2, _or_<_B3, _B4> > >::type { }; template<> struct _or_<void_type, void_type, void_type, void_type>; template<class _B1> struct _or_<_B1, void_type, void_type, void_type> : public _B1 { }; template<class _B1, class _B2> struct _or_<_B1, _B2, void_type, void_type> : public conditional<_B1::value, _B1, _B2>::type { }; template<class _B1, class _B2, class _B3> struct _or_<_B1, _B2, _B3, void_type> : public conditional<_B1::value, _B1, _or_<_B2, _B3> >::type { }; template<class _B1 = void_type, class _B2 = void_type, class _B3 = void_type, class _B4 = void_type> struct _and_; template<> struct _and_<void_type, void_type, void_type, void_type>; template<class _B1> struct _and_<_B1, void_type, void_type, void_type> : public _B1 { }; template<class _B1, class _B2> struct _and_<_B1, _B2, void_type, void_type> : public conditional<_B1::value, _B2, _B1>::type { }; template<class _B1, class _B2, class _B3> struct _and_<_B1, _B2, _B3, void_type> : public conditional<_B1::value, _and_<_B2, _B3>, _B1>::type { }; template<class _Pp> struct _not_ { static const bool value = !bool(_Pp::value); typedef const bool value_type; typedef integral_constant<bool, _not_::value == bool(true)> type; operator value_type() const { // return stored value return (value); } value_type operator()() const { // return stored value return (value); } }; } 

这里有三点值得关注:

1)务必在模板的尖括号('<'和'>')之间放置一个空格,这是很重要的,因为在C ++ 11之前,标准中尚无关于如何解释_or _ <_ B2等代码中的'>>'和'<<'的说明, _或_ <_ B3,_B4 >> ,因此几乎所有编译器都将其视为移位运算符,这会导致编译错误。

2)在某些编译器(例如Visual Studio 6.0)中,存在一个错误,该错误在于无法将void类型用作模板参数。 为此,在上面的段落中引入了一个单独的void_type类型,以替换需要默认模板参数值的void类型。

3)非常老的编译器(例如,Borland C ++ Builder)具有弯曲的实现类型bool ,在某些情况下,该类型突然“转变”为inttrue- > 1, false- > 0),以及该类型的常量静态变量类型bool (不仅限于它们),如果它们包含在模板类中。 由于所有这些混乱,因此,对于my_template_type :: static_bool_value == false样式的完全无害的比较编译器可以轻松地发出无法将'undefined type' 强制转换 为int(0)之类附魔 。 因此,有必要始终尝试明确指出要比较的值的类型,从而帮助编译器确定其处理的类型。


使用constvolatile值添加更多工作。 首先,简单地实现remove_ ...,在这里我们只是简单地对某些类型修饰符进行模板专用化-如果类型带有修饰符,则编译器必须在查看了模板的所有特定性(从上一章中回顾了SFINAE原理)后,选择最合适的(明确指出了所需的修饰符) :

 template<class _Tp> struct is_function; template<class _Tp> struct remove_const { // remove top level const qualifier typedef _Tp type; }; template<class _Tp> struct remove_const<const _Tp> { // remove top level const qualifier typedef _Tp type; }; template<class _Tp> struct remove_const<const volatile _Tp> { // remove top level const qualifier typedef volatile _Tp type; }; // remove_volatile template<class _Tp> struct remove_volatile { // remove top level volatile qualifier typedef _Tp type; }; template<class _Tp> struct remove_volatile<volatile _Tp> { // remove top level volatile qualifier typedef _Tp type; }; // remove_cv template<class _Tp> struct remove_cv { // remove top level const and volatile qualifiers typedef typename remove_const<typename remove_volatile<_Tp>::type>::type type; }; 

然后我们实现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; }; } // add_const template<class _Tp> struct add_const: public detail::_add_const_helper<_Tp, is_function<_Tp>::value> { }; template<class _Tp> struct add_const<_Tp&> { typedef _Tp & type; }; // add_volatile template<class _Tp> struct add_volatile : public detail::_add_volatile_helper<_Tp, is_function<_Tp>::value> { }; template<class _Tp> struct add_volatile<_Tp&> { typedef _Tp & type; }; // add_cv template<class _Tp> struct add_cv : public detail::_add_cv_helper<_Tp, is_function<_Tp>::value> { }; template<class _Tp> struct add_cv<_Tp&> { typedef _Tp & type; }; 

在这里,我们仔细地分别处理引用类型,以免丢失链接。 同样,我们不会忘记原则上不可能使volatileconst成为可能的函数类型,因此我们将它们保持“原样”。 我可以说所有这些看起来非常简单,但是当“魔鬼在细节中”,或者“错误在实现的细节中”时,情况就是这样。

第四章第一部分结束。 在第二部分中,我将讨论如何对编译器进行硬模板编程,并且还会有更多很棒的模板魔术。 嗯,但是-根据当今的一些编译器为什么长久以来都不是整数常数

谢谢您的关注。

Source: https://habr.com/ru/post/zh-CN417547/


All Articles