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

我们继续冒险。

先前部分的摘要


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

实现了static_assertnoexceptcountof ,并且在考虑了所有非标准定义和编译器功能之后,出现了有关当前编译器支持的功能的信息。 包含了自己的nullptr实现,该实现在编译阶段选择。

现在是使用type_traits和所有这些“特殊模板魔术”的时候了。 在本章的前面部分中,我们检查了我对标准库基本模板的实现,在这一部分中,我们将讨论SFINAE技术与模板的结合以及一些代码生成。

链接到GitHub,为不耐烦的读者和非读者提供今天的结果:
欢迎有建设性的批评和批评
cat下有更多的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 ++。 延续性


4.3指针和所有所有


在这一阶段,我只能获得有关类型是否为std :: is_array的数组的信息,并且有可能启动指针模板。 实施也是微不足道的,但并非没有假设。

// is_array template<class> struct is_array : public false_type { }; template<class _Tp, std::size_t _Size> struct is_array<_Tp[_Size]> : public true_type { }; /*template<class _Tp> struct is_array<_Tp[]>: public true_type { }; */ 

给定长度的数组的简单模板专门化可以“捕获”所有类型的数组,但是,不完整的类型T [] (未指定长度的数组)会引起问题。 事实是,在专门设计模板时,某些编译器(C ++ Builder)未定义此类型,并且我在这里还没有找到通用的解决方案。

在库被“教导”以定义内置类型,类型存储器中的对齐方式,在编译时通过模板使用类型修饰符和其他基本功能之后,就该使用指针和引用了。

图片 在C ++中,可以区分两组指针-指向类成员的指针和指向其他对象的指针。 为什么这种分离对于标准库的进一步实施很重要? 事实是,指向类成员的指针由于存在this而与其他指针有显着差异,即 指向此类的对象的指针。 而且按照标准,指向类成员的指针具有用于定义的单独语法,是单独的类型,并且不能通过常规指针表示。 实际上,这转化为以下事实:指向类成员的指针的大小通常大于常规指针的大小( == sizeof(void *) ),因为 为了实现类的虚拟成员函数以及存储this指针,编译器通常将指向类成员的指针作为结构实现( 了解虚函数和structure )。 根据标准,编译器可以自行决定是否给出指向类成员的指针的方式,但是在考虑其他代码时,我们会记住大小和表示方式上的这种差异。

要定义指向对象的常规指针,我们将编写一个简单的is_pointer模板,以及一个is_lvalue_reference模板用于对象引用( 我们将is_rvalue_reference 放在一边 ,因为直到第11个标准,都没有&&运算符以及整个move语义):

 namespace detail { template<class> struct _is_pointer_helper : public false_type { }; template<class _Tp> struct _is_pointer_helper<_Tp*> : public true_type { }; } // is_pointer template<class _Tp> struct is_pointer : public detail::_is_pointer_helper<typename remove_cv<_Tp>::type>::type { }; // is_lvalue_reference template<class> struct is_lvalue_reference : public false_type { }; template<class _Tp> struct is_lvalue_reference<_Tp&> : public true_type { }; 

这里不再有任何根本上的新内容,所有相同的操作都在本章的前面部分进行过。 让我们继续定义对象的指针-现在让我们看一下函数的指针。
重要的是要了解,根据标准,类的函数和成员函数是完全不同的实体:

  • 第一个指针将是普通的(指向对象的指针),第二个指针将具有指向类成员的指针。

 void (*func_ptr)(int); //  'func_ptr'    'void func(int){}' void (ClassType::*mem_func_ptr)(int); //  'mem_func_ptr'  -  'ClassType'  'void ClassType::func(int){}' 

  • 您可以创建到第一个链接(对象链接),但是不能创建第二个链接。

 void (&func_ref)(int); //  'func_ref'    'void func(int){}' //-------------------- //   -     
在这里,我仅提及一些有关代码生成的内容。 由于在C ++ 11之前还没有参数数目可变的模板,因此可以通过在输入端具有大量参数的主模板进行特殊化并通过默认的伪参数对其进行初始化来确定所有参数可能不同的模板 。 同样的情况也适用于函数重载,例如 也没有带有可变数量参数的宏。 由于用手编写了60-70行相同类型的模板专业化代码,因此重载函数是一项相当乏味且无用的任务,而且还充满了犯错误的可能性,为此我编写了一个简单的模板和函数重载代码生成器。 我决定限制自己将函数定义为24个参数,这在代码中看起来很麻烦,但简单明了:

 namespace detail { template <class R> struct _is_function_ptr_helper : false_type {}; template <class R > struct _is_function_ptr_helper<R(*)()> : true_type {}; template <class R > struct _is_function_ptr_helper<R(*)(...)> : true_type {}; template <class R, class T0> struct _is_function_ptr_helper<R(*)(T0)> : true_type {}; template <class R, class T0> struct _is_function_ptr_helper<R(*)(T0 ...)> : true_type {}; 

...
  template <class R, class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15, class T16, class T17, class T18, class T19, class T20, class T21, class T22, class T23, class T24> struct _is_function_ptr_helper<R(*)(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24)> : true_type {}; template <class R, class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15, class T16, class T17, class T18, class T19, class T20, class T21, class T22, class T23, class T24> struct _is_function_ptr_helper<R(*)(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24 ...)> : true_type {}; } 

我们为SFINAE技术定义了上一章中我们熟悉的类型:

 namespace detail { // SFINAE magic typedef char _yes_type; struct _no_type { char padding[8]; }; } 

为了方便起见还有一些宏
 namespace detail { #define _IS_MEM_FUN_PTR_CLR \ template <class R, class T TYPES > \ _yes_type _is_mem_function_ptr(R(T::*const volatile*)(ARGS)); \ template <class R, class T TYPES > \ _yes_type _is_mem_function_ptr(R(T::*const volatile*)(ARGS...)); \ template <class R, class T TYPES > \ _yes_type _is_mem_function_ptr(R(T::*const volatile*)(ARGS) const); \ template <class R, class T TYPES > \ _yes_type _is_mem_function_ptr(R(T::*const volatile*)(ARGS) volatile); \ template <class R, class T TYPES > \ _yes_type _is_mem_function_ptr(R(T::*const volatile*)(ARGS) const volatile); \ template <class R, class T TYPES > \ _yes_type _is_mem_function_ptr(R(T::*const volatile*)(ARGS...) const); \ template <class R, class T TYPES > \ _yes_type _is_mem_function_ptr(R(T::*const volatile*)(ARGS...) volatile); \ template <class R, class T TYPES > \ _yes_type _is_mem_function_ptr(R(T::*const volatile*)(ARGS...) const volatile); #ifdef _STDEX_CDECL _no_type _STDEX_CDECL _is_mem_function_ptr(...); #define _IS_MEM_FUN_CDECL_PTR \ template <class R, class T TYPES > \ _yes_type _is_mem_function_ptr(R(__cdecl T::*const volatile*)(ARGS)); \ template <class R, class T TYPES > \ _yes_type _is_mem_function_ptr(R(__cdecl T::*const volatile*)(ARGS) const); \ template <class R, class T TYPES > \ _yes_type _is_mem_function_ptr(R(__cdecl T::*const volatile*)(ARGS) volatile); \ template <class R, class T TYPES > \ _yes_type _is_mem_function_ptr(R(__cdecl T::*const volatile*)(ARGS) const volatile); #define _IS_MEM_FUN_STDCALL_PTR \ template <class R, class T TYPES > \ _yes_type _is_mem_function_ptr(R(__stdcall T::*const volatile*)(ARGS)); \ template <class R, class T TYPES > \ _yes_type _is_mem_function_ptr(R(__stdcall T::*const volatile*)(ARGS) const); \ template <class R, class T TYPES > \ _yes_type _is_mem_function_ptr(R(__stdcall T::*const volatile*)(ARGS) volatile); \ template <class R, class T TYPES > \ _yes_type _is_mem_function_ptr(R(__stdcall T::*const volatile*)(ARGS) const volatile); #define _IS_MEM_FUN_FASTCALL_PTR \ template <class R, class T TYPES > \ _yes_type _is_mem_function_ptr(R(__fastcall T::*const volatile*)(ARGS)); \ template <class R, class T TYPES > \ _yes_type _is_mem_function_ptr(R(__fastcall T::*const volatile*)(ARGS) const); \ template <class R, class T TYPES > \ _yes_type _is_mem_function_ptr(R(__fastcall T::*const volatile*)(ARGS) volatile); \ template <class R, class T TYPES > \ _yes_type _is_mem_function_ptr(R(__fastcall T::*const volatile*)(ARGS) const volatile); #else _no_type _is_mem_function_ptr(...); #define _IS_MEM_FUN_CDECL_PTR #define _IS_MEM_FUN_STDCALL_PTR #define _IS_MEM_FUN_FASTCALL_PTR #endif #define _IS_MEM_FUN_PTR \ _IS_MEM_FUN_PTR_CLR \ _IS_MEM_FUN_CDECL_PTR \ _IS_MEM_FUN_STDCALL_PTR \ _IS_MEM_FUN_FASTCALL_PTR } 


定义了宏,以便重新定义TYPESARGS定义为类型和参数的列表,然后用_IS_MEM_FUN_PTR宏替换预处理器为所有可能的函数类型生成定义,相对方便。 还值得关注的事实是,对于Microsoft的编译器来说, 调用协议__fastcall__stdcall__cdecl )也很重要 ,因为 使用不同的约定,函数将有所不同,尽管它们具有相同的参数集和返回值。 结果,整个宏宏设计非常紧凑地使用:

 namespace detail { #define TYPES #define ARGS _IS_MEM_FUN_PTR #undef TYPES #undef ARGS #define TYPES , class T0 #define ARGS T0 _IS_MEM_FUN_PTR #undef TYPES #undef ARGS #define TYPES , class T0, class T1 #define ARGS T0, T1 _IS_MEM_FUN_PTR #undef TYPES #undef ARGS 

...
  #define TYPES , class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15, class T16, class T17, class T18, class T19, class T20, class T21, class T22, class T23, class T24 #define ARGS T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24 _IS_MEM_FUN_PTR #undef TYPES #undef ARGS //      define  : #undef _IS_MEM_FUN_PTR #undef _IS_MEM_FUN_PTR_CLR #undef _IS_MEM_FUN_CDECL_PTR #undef _IS_MEM_FUN_STDCALL_PTR #undef _IS_MEM_FUN_FASTCALL_PTR } 

现在,所有内容都写成:

 namespace detail { template <class _Tp, bool _IsRef> struct _is_mem_function_ptr_impl { static _Tp *p; static const bool value = (sizeof(_is_mem_function_ptr(_is_mem_function_ptr_impl::p)) == sizeof(_yes_type)); typedef typename integral_constant<bool, _is_mem_function_ptr_impl::value == bool(true)>::type type; }; template <class _Tp> struct _is_mem_function_ptr_impl<_Tp, true>: public false_type {}; template <class _Tp> struct _is_mem_function_ptr_helper: public _is_mem_function_ptr_impl<_Tp, is_reference<_Tp>::value>::type {}; template <class _Tp, bool _IsMemberFunctionPtr> struct _is_function_chooser_impl : public false_type { }; template <class _Tp> struct _is_function_chooser_impl<_Tp, false> : public _is_function_ptr_helper<_Tp*> { }; template<class _Tp, bool _IsRef = true> struct _is_function_chooser : public false_type { }; template <class _Tp> struct _is_function_chooser<_Tp, false> { static const bool value = _is_function_chooser_impl<_Tp, _is_mem_function_ptr_helper<_Tp>::value>::value; }; } 

要检查类型是否为类的成员函数,首先要检查该类型是否为引用。 然后,将创建此类型的指针并将其替换为probe函数。 使用SFINAE技术,编译器为此类指针选择探针函数的必要重载,并根据与_yes_type的比较结果形成结果。

基于对类的成员函数的检查,对类型是否属于函数类型进行类型检查。 我们检查类型是否为引用,如果不是,则为该类型的指针寻找合适的模板探针结构专业化,对于具有最多24个参数的任何函数指针,其均为true_type

现在,我们使用结果来实现is_function 。 在这里,由于与上一部分相同的原因,我无法从integral_constant继承此结构,因此“模拟”了integrated_constant的行为。

 // is_function template<class _Tp> struct is_function { static const bool value = detail::_is_function_chooser<_Tp, is_reference<_Tp>::value>::value; typedef const bool value_type; typedef integral_constant<bool, is_function::value == bool(true)> type; operator value_type() const { // return stored value return (value); } value_type operator()() const { // return stored value return (value); } }; 

为了实现is_member_function_pointer,它仍然更加简单:

 // is_member_function_pointer template<class _Tp> struct is_member_function_pointer : public detail::_is_mem_function_ptr_helper<typename remove_cv<_Tp>::type>::type { }; 

此外,基于这些模式,原则上我们可以确定类型是否是该类的成员:

 namespace detail { template<class _Tp> struct _is_member_object_pointer_impl1 : public _not_< _or_<_is_function_ptr_helper<_Tp>, _is_mem_function_ptr_helper<_Tp> > >::type { }; template<class _Tp> struct _is_member_object_pointer_impl2 : public false_type { }; template<class _Tp, class _Cp> struct _is_member_object_pointer_impl2<_Tp _Cp::*> : public true_type { }; template<class _Tp> struct _is_member_object_pointer_helper: public _and_<_is_member_object_pointer_impl1<_Tp>, _is_member_object_pointer_impl2<_Tp> >::type {}; } // is_member_object_pointer template<class _Tp> struct is_member_object_pointer : public detail::_is_member_object_pointer_helper<typename remove_cv<_Tp>::type>::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); } }; } 


在这里,我们对类型使用逻辑运算,这些运算在条件模板的帮助最终选择合适的模板类型。 因此,在编译阶段,模板编程的所有辉煌之处,我们已经掌握了有关类型是否为类成员的信息。 相当“愤怒”,但多么壮观和有效!

在相同的逻辑元素上进行更多的纯模板编程,我们具有is_fundamentalis_compound等。 迹象(这使我高兴,但是您呢?):

 // is_arithmetic template<class _Tp> struct is_arithmetic : public detail::_or_<is_integral<_Tp>, is_floating_point<_Tp> >::type { }; // is_fundamental template<class _Tp> struct is_fundamental : public detail::_or_<is_arithmetic<_Tp>, is_void<_Tp>, is_null_pointer<_Tp> >::type {}; // is_object template<class _Tp> struct is_object : public detail::_not_< detail::_or_< is_function<_Tp>, is_reference<_Tp>, is_void<_Tp> > >::type {}; // is_scalar template<class _Tp> struct is_scalar : public detail::_or_<is_arithmetic<_Tp>, is_pointer<_Tp>, is_member_pointer<_Tp>, is_null_pointer<_Tp>/*, is_enum<_Tp>*/ >::type {}; // is_compound template<class _Tp> struct is_compound: public detail::_not_<is_fundamental<_Tp> >::type { }; 
细心的读者会注意到is_enum的定义已被注释掉。 事实是我没有找到将枚举与其他类型区分开的方法,但是我认为如果不使用依赖于编译器的宏,这是可行的。 也许一位细心而有见识的读者会在这方面告诉您您的方式或思路。
要确定类型是类这一事实,现在就不再需要:

 namespace detail { template <class _Tp, bool _IsReference> struct _is_class_helper { typedef integral_constant<bool, false> type; }; template <class _Tp> struct _is_class_helper<_Tp, false> { typedef integral_constant<bool, (is_scalar<_Tp>::value == bool(false)) //&& !is_union<_Tp>::value >::value && (is_array<_Tp>::value == bool(false)) && (is_void<_Tp>::value == bool(false)) && (is_function<_Tp>::value == bool(false))> type; }; } // is_class template<class _Tp> struct is_class : public detail::_is_class_helper<typename remove_cv<_Tp>::type, is_reference<_Tp>::value>::type { }; 

一切都会好起来的,但是在一般情况下,C ++中的并集不能与类区别开来。 因为它们的“外部表现形式”非常相似,并且没有编译错误,我无法验证差异(例如,无法从union继承)。 也许有人会告诉您一个棘手的方法来确定编译时的并集,然后is_class将完全符合标准。

在本章的最后一部分 ,我将讨论如何实现std ::衰减std :: common_type ,以及还有哪些要添加到type_traits中

谢谢您的关注。

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


All Articles