C ++ 11元组实现详细信息(2012)

2021-02-06 20:23:48

Warning: Can only detect less than 5000 characters

非递归元组实现背后的基本思想是,元组元素存储在TupleLeaf基类中,但是递归实现使用深层次的类层次结构,而我们将使用多重继承。用伪代码:

模板< typename T0,typename T1,...,typename Tn>类PseudoTuple:TupleLeaf< 0,T0>,TupleLeaf< 1,T1>,...,TupleLeaf< n,Tn> {...};

每个叶子都有一个索引,因此即使每个基类所包含的类型相同,它们也都将是唯一的,因此我们可以使用简单的static_cast访问n元素:

// TupleLeaftemplate< size_t I_,类型名称ValueType_,布尔值IsEmpty_ = is_empty< ValueType_> :: value#if __has_feature(is_final)&& !__ is_final(ValueType _)#endif> class TupleLeaf;模板< size_t I,类型名ValueType,bool IsEmpty>内联void swap(TupleLeaf< I,ValueType,IsEmpty& a,TupleLeaf< I,ValueType,IsEmpty>& b){swap(a.get(),b.get());}模板< size_t I_,类型名ValueType_,bool IsEmpty_> class TupleLeaf {ValueType_ value_;元宝叶运算符=(const TupleLeaf&)= delete; public:TupleLeaf():value_(){static_assert(!is_reference< ValueType_> :: value,"尝试默认在元组中构造引用元素"); } TupleLeaf(const TupleLeaf& t):value_(t.get()){static_assert(!is_rvalue_reference< ValueType_> :: value,"无法复制带有右值引用成员的元组"); }模板< typename T,typename = typename enable_if< is_constructible< ValueType_,T> :: value> :: type>显式TupleLeaf(T& t):value_(forward< T>(t)){static_assert(!is_reference< ValueType_> :: value ||(is_lvalue_reference< ValueType_> :: value&&(is_lvalue_reference< T> :: value || is_same< typename remove_reference< T> :: type,reference_wrapper< typename remove_reference< ValueType_> :: type>> :: value))||(is_rvalue_reference< ValueType_> :: value&&amp ;! is_lvalue_reference< T> :: value),"试图在具有rvalue"的元组中构造引用元素。 }模板< typename T>显式TupleLeaf(const TupleLeaf< I_,T& t):value_(t.get()){} template< typename T>元宝叶运算符=(T& t){value_ = forward T(t);返回* this; } int swap(TupleLeaf& t){migl2 :: detail :: swap(* this,t);返回0; } ValueType_& get(){返回value_; } const ValueType_& get()const {return value_; }};

在这里,我们声明了TupleLeaf模板,并在可以使用Empty Base Class Optimization时将IsEmpty值设置为true。这里需要一些技巧:如果实现支持最​​终类,那么我们必须检查ValueType_是否为最终类。由于我们不能将最终类作为子类,因此在这种情况下我们不能使用EBCO。

通过交换包含的元素交换两个TupleLeafs。我们将从交换成员函数中使用它。

适用于一般情况的TupleLeaf实现(无空基类优化)。我们将禁用正常的复制分配(第17行),因为我们将始终使用转发复制分配(第53-57行)。唯一有趣的是交换的返回类型(第59-62行)。通常,它应该是空的,但是稍后我们将使用模板包扩展从TupleImpl :: swap中调用此swap成员,并为此需要一个具有返回类型的函数。请参阅下面的吞咽功能。

接下来,我们专门针对TupleLeaf进行使用空基类优化的情况,以免浪费空Tuple成员的空间:

// TupleLeaf专业化,以防我们可以使用空基类优化:template< size_t I_,typename ValueType_> class TupleLeaf< I_,ValueType_,true> :私有ValueType_ {TupleLeaf&运算符=(const TupleLeaf&)= delete; public:TupleLeaf(){} TupleLeaf(const TupleLeaf&t):ValueType_(t.get()){static_assert(!is_rvalue_reference< ValueType_> :: value," Can不复制带有右值引用成员的元组"); }模板< typename T,typename = typename enable_if< is_constructible< ValueType_,T> :: value> :: type>显式TupleLeaf(T& t):ValueType_(forward< T>(t)){}模板< typename T>显式TupleLeaf(const TupleLeaf< I_,T& t):ValueType_(t.get()){} template< typename T>元宝叶operator =(T& t){ValueType _ :: operator =(forward< T>(t));返回* this; } int swap(TupleLeaf& t){migl2 :: detail :: swap(* this,t);返回0; } ValueType_& get(){return static_cast< ValueType_&>(* this); } const ValueType_& get()const {return static_cast< const ValueType_&>(* this); }};

我们需要TupleLeafs的索引和相应的类型。为了处理它们的耦合,我们将创建两个用于存储它们的帮助程序类以及在需要时创建它们的相应模板:

// TupleIndexestemplate< size_t ... Is_> struct TupleIndexes {}; template< size_t Start_,类型名称TupleIndexes_,size_t End_> struct MakeTupleIndexesImpl; template< size_t Start_,size_t ... ,TupleIndexes< Indexes _...>,End_> {typedef typename MakeTupleIndexesImpl< Start_ + 1,TupleIndexes< Indexes _...,Start_&gt ;, End_> :: type type;};模板< size_t End_,size_t ... Indexes_>结构MakeTupleIndexesImpl< End_,TupleIndexes< Indexes_。 。>,End_> {typedef TupleIndexes< Indexes _...>类型;};模板< size_t End_,size_t Start_ = 0>结构MakeTupleIndexes {static_assert(Start_< = End_," MakeTupleIndexes:无效参数"); typedef typename MakeTupleIndexesImpl< Start_,TupleIndexes<&gt ;, End_> :: type type;};

MakeTupleIndexes创建用于编码从Start_到End_的索引的类型,例如:TupleTypes< 0,1,2>。可能会更简单一些,但是我们将使用一个构造函数来扩展标准元组,该构造函数所使用的参数要少于实际的元组元素数量(默认情况下构造其余的),为此,我们需要能够从任意索引而不是0开始创建TupleIndexes。下面的MakeTupleTypes也是如此。这两个模板都使用帮助程序模板来逐步建立其值。最终结果是用于完整索引和类型的typedef。

注意:自撰写本文以来,c ++ 14引入了std :: index_sequence,因为发现该概念在许多情况下都是有用的。序列生成的实现也可以被优化:代替上述线性递归,可以使用对数实现,或者序列生成甚至可以是编译器固有的。元组实现可以在此处更新为使用新的index_sequence。

// TupleTypestemplate< typename ... Ts_> struct TupleTypes {}; // TupleSizetemplate< typename T_> struct TupleSize:public tuple_size< T_> {}; template< typename T_> struct TupleSize< const T_> :公共TupleSize< T_> {}; template< typename T_> struct TupleSize< volatile T_> :公共TupleSize< T_> {}; template< typename T_> struct TupleSize< const volatile T_> :公共TupleSize< T_> {};模板<类型名... Ts_>结构TupleSize< TupleTypes< Ts _...>> :公共integer_constant< size_t,sizeof ...(Ts _)> {}; // Tupletemplate< typename ... Ts_> struct TupleSize< Tuple< Ts _...>的特殊化; :公共integer_constant< size_t,sizeof ...(Ts _)> {};

在这里,我们声明了特殊的类型持有者TupleTypes,然后可以使用sizeof ...运算符为其定义TupleSize。我们自己的Tuple的TupleSize使用TupleTypes。对于所有其他类型,我们都使用标准的tuple_size模板,因此我们的内部TupleSize也将适用于std :: tuple,std :: pair和std :: array。符合cv要求的类型将转发给不符合要求的TupleTypes的实现。

类似地,我们的内部TupleElement是为TupleTypes和Tuple定义的,但是回退到其他类型的标准tuple_element上:

// TupleElementtemplate< size_t I_,类型名T_> class TupleElement:public tuple_element< I_,T_> {}; template< size_t I_> class TupleElement< I_,TupleTypes<> {public:static_assert(I _!= I _," tuple_element索引超出范围");};模板<类型名H_,类型名... Ts_>类TupleElement< 0,TupleTypes< H_,Ts_。 。>> {public:typedef H_type;};模板< size_t I_,typename H_,typename ... Ts_> class TupleElement< I_,TupleTypes< H_,Ts _...>> {public:typedef typename TupleElement< I-1,TupleTypes< Ts _...> :: type type;}; // Tupletemplate的特殊化< size_t I_,typename ... Ts_>类TupleElement< I_,Tuple< ; Ts _...>> {public:typedef typename TupleElement< I_,TupleTypes< Ts _...> :: type type;};模板< size_t I_,typename ... Ts_> class TupleElement< I_,const Tuple< Ts _... >> {public:typedef typename add_const< typename TupleElement< I_,TupleTypes< Ts _...>> :: type> :: type type;};模板< size_t I_,typename ... Ts_> class TupleElement< I_,挥发性元组Ts _...> {public:typedef typename add_volatile< typename TupleElement< I_,TupleTypes< Ts _...> :: type> :: type type;};模板< size_t I_,typename ... Ts_> class TupleElement< I_, const volatile Tuple Ts _...> {public:typedef typename add_cv< typename TupleElement< I_,TupleTypes< Ts _...>> :: type> :: type type;};

TupleElement是一个经典的递归模板。当我们减少索引时(在主模板中的I_,第19-23行),我们从列表的开头切掉了这些类型。停止条件是当我们达到I_ == 0(第13-17行)时。如果其余的TupleTypes在达到停止条件之前为空,则将通过static_assert发出错误信号(第7-11行)。最终结果(如果一切顺利的话)是与我们的TupleTypes中的I_元素相对应的typedef(TupleElement :: type)。我们最终的Tuple的专业化使用针对const和volatile限定符调整的TupleType。

注意:可以通过让重载分辨率为我们找到正确的类型而不是上面介绍的线性递归来实现TupleElement。有关详细信息,请参阅其他有关元组的不错的博客文章。

// MakeTupleTypestemplate< typename TupleTypes_,typename Tuple_,size_t Start_,size_t End_> struct MakeTupleTypesImpl; template< typename ... Types_,typename Tuple_,size_t Start_,size_t End_> struct MakeTupleTypesImpl< TupleTypes ...< Tuple_,Start_,End_> {typedef typename remove_reference< Tuple_> :: type TupleType; typedef typename MakeTupleTypesImpl< TupleTypes< Types _...,typename conditional< is_lvalue_reference< Tuple_> :: value,//如果tuple_是ref类型名,则附加ref TupleElement< Start_,TupleType> :: type& TupleElement< Start_,TupleType> :: type> :: type&gt ;, Tuple_,Start_ + 1,End_> :: type type;};模板< typename ... Types_,typename Tuple_,size_t End_>结构MakeTupleTypesImpl< TupleTypes< Types _...>,Tuple_,End_,End_> {typedef TupleTypes< Types _...> type;};模板< typename Tuple_,size_t End_ = TupleSize< typename remove_reference< Tuple_> :: type> :: value,size_t Start_ = 0> struct MakeTupleTypes {static_assert(Start_< = End_," MakeTupleTypes:无效的参数"); typedef typename MakeTupleTypesImpl< TupleTypes<&gt ;, Tuple_,Start_,End_> :: type type;};

MakeTupleTypes从[Start_ .. End_]范围内的Tuple_中提取类型,并将其存储在我们的TupleTypes类型中。元素类型由上面定义的TupleElement确定,并附加到临时列表中。由于std :: tuple_element(因此,我们的TupleElement)不适用于引用类型,因此我们将typedef用作Tuple_的引用剥离类型(第8行的TupleType),但是我们将结果类型从TupleElement转换为a如果原始Tuple_是lvalue_reference(此转换不会影响原元组中已经为引用类型的类型),则为reference。

我们已经准备好将这些片段组装到我们内部的TupleImpl类中,该类将成为最终Tuple的基础。

// TupleImpltemplate< typename ... Ts> void swallow(Ts& ...){} template< typename TupleIndexes_,typename ... Ts_> struct TupleImpl; template< size_t ... Indexes_,typename。 .. Ts_>结构TupleImpl< TupleIndexes< Indexes _...>,Ts _...> :public TupleLeaf< Indexes_,Ts_> ... {模板< size_t ... FirstIndexes,类型名... FirstTypes,size_t ... LastIndexes,类型名... LastTypes,类型名... ValueTypes>显式TupleImpl(TupleIndexes< FirstIndexes ...>,TupleTypes< FirstTypes ...>,TupleIndexes< LastIndexes ...>,TupleTypes< LastTypes ...>,ValueTypes& amp; ...值):TupleLeaf< ; FirstIndexes,FirstTypes>(forward< ValueTypes>(值))...,TupleLeaf< LastIndexes,LastTypes>()... {}模板< typename OtherTuple> TupleImpl(OtherTuple& t):TupleLeaf< Indexes_,Ts_>(转发类型名称TupleElement< Indexes_,类型名称MakeTupleTypes< OtherTuple> :: type> :: type>(get< Indexes_>(t)))... {}模板< typename OtherTuple> TupleImpl& operator =(OtherTuple& t){swallow(TupleLeaf< Indexes_,Ts_> :: operator =(forward< typename TupleElement< Indexes_,typename MakeTupleTypes< OtherTuple> :: type> :: type>(get< Indexes_> t)))...);返回* this; } TupleImpl& operator =(const TupleImpl& t){swallow(TupleLeaf< Indexes_,Ts_> :: operator =(static_cast< const TupleLeaf< Indexes_,Ts_&>(t).get())...);返回* this; } void swap(TupleImpl& t){swallow(TupleLeaf< Indexes_,Ts_> :: swap(static_cast< TupleLeaf< Indexes_,Ts_>>(t))...); }};

swallow()函数只是一个小技巧,它允许我们在不可能的地方使用template-parameter-pack扩展:当我们要为template-parameter的每个元素调用foo()时-pack,我们用这样的扩展调用swallow():swallow()的参数绑定到除void以外的任何内容,因此我们需要确保foo()确实返回某些内容(不是void foo( ))。这就是为什么我们有奇怪的原因TupleLeaf :: swap()函数的签名。由于功能本身

......