`

boost源码剖析之:泛型编程精灵type_traits(rev#2)

 
阅读更多

boost源码剖析之:泛型编程精灵type_traits(rev#2)

刘未鹏

C++的罗浮宫(http://blog.csdn.net/pongba)

动机

使用traits的动机一般有三种,分派、效率、使某些代码通过编译。

分派

下面有一个模板函数,假设一个动物收容组织提供了它,他们接受所有无家可归的可怜的小动物,于是他们向外界提供了一个函数接受注册。函数看起来像这样:

template<class T> // T表示接受的是何种动物

void AcceptAnimals(T animal)

{

... //do something

};

但是,如果他们想将猫和狗分开处理(毕竟饲养一只猫和饲养一只狗并不相同。他们可能会为狗买一根链子,而温顺的猫则可能不需要)。一个可行的方法是分别提供两个函数:AcceptDogAcceptCat,然而这种解决办法并不优雅(想想看,注册者可能既有一只猫又有一只狗,这样他不得不调用不同的函数来注册,而且,如果种类还在增多呢,那样会导致向外提供的接口的增多,注册者因此而不得不记住那些烦琐的名字,而这显然没有只需记住AccpetAnimal这一个名字简单)。如果想保持这个模板函数,并将它作为向外界提供的唯一接口,则我们需要某种方式来获取类型T的特征(trait),并按照不同的特征来采用不同的策略。这里我们有第二个解决办法

约定所有的动物类(class Cat,class Dog)都必须在内部typedef一个表明自己身份的类型,作为标识的类型如下:

struct cat_tag{}; //这只是个空类,目的是激发函数重载,后面会解释

struct dog_tag{}; //同上

于是,所有狗类都必须像这样:

class Dog

{

public:

// 类型(身份)标志,表示这是狗类,如果是猫类则为typedef cat_tag type;

typedef dog_tag type;

...

}

然后,动物收容组织可以在内部提供对猫狗分开处理的函数,像这样:

// 第二个参数为无名参数,只是为了激发函数重载

template<class T>

void Accept(T dog,dog_tag)

{...}

template<class T>

void Accpet(T cat,cat_tag) // 同上

{...}

于是先前的Accept函数可以改写如下:

template<class T>

void Accept(T animal) //这是向外界提供的唯一接口

{

// 如果T为狗类,则typename T::type就是dog_tag,那么typename T::type()就是创建了一个dog_tag类的临时对象,根据函数重载的规则,这将调用Accept(T,dog_tag),这正是转向处理狗的策略。如果T为猫类,则typename T::typecat_tag,由上面的推导,这将调用Accept(T,cat_tag),即转向处理猫的策略,typename 关键字告诉编译器T::type是个类型而不是静态成员。

Accept(animal, typename T::type()); // #1

}

所有类型推导,函数重载,都在编译期完成,你几乎不用耗费任何运行期成本(除了创建dog_tag,cat_tag临时对象的成本,然而经过编译器的优化,这种成本可能也会消失)就拥有了可读性和可维护性高的代码。但是,等等!你说:“traits在哪?typename T::type其实就是traits,只不过少了一层封装而已,如果像这样作一些改进:

template<typename T>

struct AnimalTraits

{

typedef T::type type;

};

于是,#1处的代码便可以写成:

Accept(animal, typename AnimalTraits<T>::type());

效率

通常为了提高效率,为某种情况采取特殊的措施是必要的,例如STL里面的copy,原型像这样:

// [first,last)区间内的元素拷贝到以dest开始的地方

template<typename IterIn,typename IterOut>

IterOut copy(IterIn first,IterIn last,IterOut dest){

// ptr_category用来萃取出迭代器的类别以进行适当程度的优化

return copy_opt(first,last,dest, ptr_category(first,dest));

}

copy_opt有两个版本,其中一个是针对如基本类型的数组作优化的,如果拷贝发生在char数组间,那么根本用不着挨个元素赋值,基于数组在内存中分布的连续性,可以用速度极快的memmove函数来完成。ptr_category有很多重载版本,对可以使用memmove的情况返回一个空类如scalar_ptr的对象以激发函数重载。其原始版本则返回空类non_scalar_ptr的对象。copy_opt的两个版本于是像这样:

// 使用memmove

template<typename IterIn,typename IterOut>

IterOut copy(IterIn first,IterIn last,IterOut dest,

scalar_ptr)

{ ...}

// 按部就班的逐个拷贝

template<typename IterIn,typename IterOut>

IterOut copy(IterIn first,IterIn last,IterOut dest,

non_scalar_ptr)

{ ...}

其实通常为了提高效率,还是需要分派。

使某些代码能通过编译

这或许令人费解,原来不能通过编译的代码,经过traits的作用就能编译了吗?是的,考虑std::pair的代码(为使代码简洁,忽略大部分)

template <typename T1, typename T2>

struct pair

{

T1 first;

T2 second;

// 如果T1T2本身是引用,则编译错误,因为没有“引用的引用

pair(const T1 & nfirst, const T2 & nsecond) // #2

:first(nfirst), second(nsecond) { }

};

这里可以使用一个traits(boost库里面的名字为add_reference)来避免这样的错误。这个traits内含一个typedef,如果add_reference<T>T为引用,则typedef T type;如果不是引用,则typedef T& type;这样#2处的代码便可改成:

pair(add_reference<const T1>::type nfirst,

add_reference<const T2>::type nsecond)

...

这对所有的类型都能通过编译。

boost库中的traits

boost中的Traits十分完善,可分为如下几大类:

1. Primary Type Categorisation(初级类型分类)

2. Secondary Type Categorisation(次级类型分类)

3. Type Properties(类型属性)

4. Relationships Between Types(类型间关系)

5. Transformations Between Types(类型间转换)

6. Synthesizing Types(类型合成)

7. Function Traits(函数traits)

由于其中一些traits只是简单的模板偏特化,故不作介绍,本文仅介绍一些技术性较强的traits。由于traits的定义往往重复代码较多,所以必要时本文仅剖析其底层机制。所有源码均摘自相应头文件中,为使源码简洁,所有的宏均已展开。由于traits技巧与编译平台息息相关,某些平台可能不支持模板偏特化。这里我们假设编译器是符合C++标准的。在我的VC7.0上,以下代码均通过编译并正常工作。

初级类型分类

is_array (boost/type_traits/is_array.hpp)

定义

// 缺省

template<typename T>

struct is_array

{

static const bool value=false;

};

// 偏特化

template<typename T,size_t N>

struct is_array<T[N]>

{

static const bool value=true;

};

注解

C++标准允许整型常量表达式作为模板参数,上面的N就是这样。这也说明出现在模板偏特化版本中的模板参数(在本例中为typename T,size_t N两个)个数不一定要跟缺省的(本例中为typename T一个)相同但出现在类名称后面的参数个数却要跟缺省的个数相同(is_array<T[N]>T[N]为一个参数与缺省的个数相同)

使用

is_array<int [10]>::value // true(T=int,N=10)

is_array<int>::value // false(T=int)

is_class(.../is_class.hpp)

定义

// 底层实现,原因是根据不同的编译环境可能有不同的底层实现,我的编译环境为VC7.0,其他底层实现从略。

template <typename T>

struct is_class_impl

{

template <class U>

static ...::yes_type is_class_tester(void(U::*)(void));

template <class U> static ...::no_type is_class_tester(...);

// ice_and是一个元函数,提供逻辑与(AND)操作

static const bool value =

...::ice_and<

sizeof(is_class_tester<T>(0))==sizeof(...::yes_type), // #3

...::ice_not<...::is_union<T>::value >::value

>::value

};

template<typename T>

struct is_class

{

// 所有实现都在is_class_imp

static const bool value = is_class_impl<T>::value;

};

注解

::boost::type_traits::yes_type是一个typedef:

typedef char yes_type;

因此sizeof(yes_type)1.

::boost::type_traits::no_type是一个struct:

struct no_type

{

char padding[8];

};

因此sizeof(no_type)8

这两个类型一般被用作重载函数的返回值类型,这样通过检查返回值类型的大小就知道到底调用了哪个函数,它们的定义位于“boost/type_traits/detail/yes_no_type.hpp中。

is_class_impl中有两个static函数,第一个函数仅当模板参数U是类时才能够被实例化,因为它的参数类型是void(U::*)(void),即指向成员函数的指针。第二个函数具有不定量任意参数列表,C++标准说只有当其它所有的重载版本都不能匹配时,具有任意参数列表(...)的重载版本才会被匹配。所以,如果T为类,则void (T::*)(void)这种类型就存在,所以对is_class_tester<T>(0)的重载决议将是调用第一个函数,因为将0赋给任意类型的指针都是合法的。而如果T不是类,则就不存在void(T::*)(void)这种指针类型,所以第一个函数就不能实例化,这样,is_class_tester<T>(0)的重载决议结果只能调用第二个函数。

现在注意#3处的表达式:

sizeof(is_class_tester<T>(0))==sizeof(...::yes_type) // #3

按照上面的推导,如果T为类,is_class_tester<T>(0)实际调用第一个重载版本,返回yes_type,则该表达式求值为true。如果T不是类,则is_class_tester<T>(0)调用第二个重载版本,返回no_type,则该表达式求值为false。这正是我们想要的。

一个值得注意的地方是:在sizeof的世界里,没有表达式被真正求值编译器只推导出表达式的结果的类型,然后给出该类型的大小。

比如,对于sizeof(is_class_tester<T>(0))编译器实际并不调用函数的代码来求值,而只关心函数的返回值类型。所以声明该函数就够了。另一个值得注意之处是is_class_tester的两个重载版本都用了模板函数的形式。第一个版本用模板形式的原因是如果不那样做,而是这样

static yes_type is_class_tester(void(T::*)(void));

的话,则当T不是类时,该traits将不能通过编译,原因很简单,当T不是类时void (T::*)(void)根本不存在。然而,使用模板时,当T不是类时该重载版本会因不能实例化而根本不编译,C++标准允许不被使用的模板不编译(实例化)。这样编译器就只能使用第二个版本,这正合我们的意思。

is_class_tester的第二个重载版本为模板则是因为第一个版本是模板,因为在#3处对is_class_tester的调用是这样的:

is_class_tester<T>(0)

如果第二版本不是模板的话,这样调用只能解析为对is_class_tester模板函数(即第一个版本)的调用,于是重载解析也就不复存在了。

等等!你意识到了一些问题:模板函数的调用可以不用显式指定模板参数!好吧,也就是说你试图这样写:

// 模板

template <class U>

static ...::yes_type is_class_tester(void(U::*)(void));

// 非模板

static ...::no_type is_class_tester(...);

然后在#3标记的那一行这样调用:

is_class_tester(0) // 原来是is_class_tester<T>(0))

是的,我得承认,这的确构成了函数重载的条件,也的确令人欣喜的通过了编译,然而结果肯定不是你想要的。你会发现对所有类型Tis_class<T>::value现在都是0

也就是说,编译器总是调用is_class_tester(..);这是因为,当调用的函数的所有重载版本中有一个或多个为模板时,编译器首先要尝试进行模板函数实例化而非重载决议,而在尝试实例化的过程中,编译器会进行模板参数推导,0的类型被编译器推导为int(0虽然可以赋给指针,但0的类型不可能被推导为指针类型,因为指针类型可能有无数种,而事实上C++是强类型语言,对象只能属于某一种类型),而第一个函数的参数类型void (U::*)(void)根本无法与int匹配(因为如果匹配了,那么模板参数U被推导为什么呢?)。所以第一个版本实例化失败后编译器只能采用非模板的第二个版本。结果如你所见,是令人懊恼的。然而如果你写的是is_class_tester<T>(0)你其实是显式实例化了is_class_tester每一个模板函数(除了那些不能以T为模板参数实例化的),而它们都被列入接受重载决议的侯选单,然后编译器要做的就只剩下重载决议了。(关于编译器在含有模板函数的重载版本时是如何进行重载决议的,可参见C++ PrimerFunction Templates一节,里面有极其详细的介绍)

以上所将的利用函数重载来达到某些目的的技术在type_traits甚至整个boost库里多处用到。

初级类型分类还有:

is_void is_integral is_float is_pointer is_reference is_union is_enum is_function

请参见boost提供的文档。

次级类型分类

is_member_function_pointer(.../is_member_function_pointer.hpp)

定义(.../detail/is_mem_fun_pointer_impl.hpp)

// 缺省版本

template <typename T>

struct is_mem_fun_pointer_impl

{

static const bool value = false;

};

// 偏特化版本,匹配无参数的成员函数

template <class R, class T >

struct is_mem_fun_pointer_impl<R (T::*)() >

{

static const bool value = true;

};

//匹配一个参数的成员函数

template <class R, class T , class T0>

struct is_mem_fun_pointer_impl<R (T::*)( T0) >

{

static const bool value = true;

};

... // 其它版本只是匹配不同参数个数的成员函数的偏特化而已,参见源文件。

template<class T>

struct is_mem_function_pointer

{

static const bool value =

is_mem_fun_pointer_impl<T>::value;

};

注解

假设你有一个类X,你这样判断:

is_mem_function_pointer<int (X::*)(int)>::value

则编译器会先将is_mem_function_pointer的模板参数class T推导为int (X::*)(int),然后将其传给is_mem_fun_pointer_impl,随后编译器寻找后者的偏特化版本中最佳匹配项为:

is_mem_fun_pointer_impl<R(T::*)(T0)>

其中R=int,T=X,T0=int。而该偏特化版本的::value=true

次级类型分类还有:

is_arithmetic is_fundamental is_object is_scalar is_compound

请参见boost提供的文档。

类型属性

is_empty(.../is_empty.hpp)

定义

// 如果T是空类,那么派生类的大小就是派生部分的大小即sizeof(int)*256

template <typename T>

struct empty_helper_t1

: public T

{

empty_helper_t1();

int i[256];

};

struct empty_helper_t2

{

int i[256];

}; // 大小为sizeof(int)*256

通过比较以上两个类的大小可以判断T是否为空类,如果它们大小相等则T为空类。反之则不为空。

这里一个值得注意的地方是:若定义一个空类E,则sizeof(E)1(这一个字节是用于在内存中唯一标识该类的不同对象。如果sizeof(E)0,则意味着不同的对象在内存中的位置没有区别,这显然有违直观)。然而如果有另一个非空类继承自E,那么这一个字节的内存就不需要。也就是说派生类的大小等于派生部分的大小,而非加上一个字节。

// 这个辅助类的作用是:如果T不是类则使用该缺省版本如果T是类则使用下面的偏特化版本。而判断T是否为类的工作则由上面讲过的is_class<>traits来做。

template <typename T, bool is_a_class = false>

struct empty_helper

{

static const bool value = false;

};

template <typename T>

struct empty_helper<T, true> // #5

{

static const bool value =

(sizeof(empty_helper_t1<T>) == sizeof(empty_helper_t2));

};

template <typename T>

struct is_empty_impl

{

// remove_cvTconst volatile属性去掉,这是因为在作为基类的类型不能有const/volatile修饰。

typedef typename remove_cv<T>::type cvt;

static const bool value =

ice_or<

empty_helper<cvt, is_class<T>::value>::value, // #4

BOOST_IS_EMPTY(cvt)

>::value;

};

注解

#4处,如果is_class<T>::valuetrue(T为类)empty_helper<cvt,is_class<T>::value>::value实际决议为empty_helper<cvt,true>,这将采用偏特化版本#5则结论出现。

否则T不是类,则采用缺省版本,结果::valuefalse

is_polymorphic(.../is_polymorphic.hpp)

is_plymorphic的运作机制基于一个基本事实:一个多态的类里面会有一个虚函数表指针(一般称为vptr),它指向一个虚函数表(一般称为vtbl)。后者保存着一系列指向虚函数的函数指针以及运行时类型识别信息。一个虚函数表指针通常占用4个字节(32寻址环境下的所有指针都占用4个字节)。反之,如果该类不是多态,则没有这个指针的开销。基于这个原理,我们可以断定:如果类X不是多态类(没有vtblvptr),则如果从它派生一个类YY中仅含有一个虚函数,这会导致sizeof(Y)>sizeof(X)(这是因为虚函数的首次出现导致编译器必须在Y中加入vptr的缘故)。反之,如果X原本就是多态类,则sizeof(Y)==sizeof(X)(因为这种情况下,Y中其实已经有了从X继承而来的vtblvptr,编译器所要做的只是将新增的虚函数纳入到vtbl中去)

定义

// T为类时使用这个版本

template <class T>

struct is_polymorphic_imp1

{

typedef typename remove_cv<T>::type ncvT;

// ncvT是将Tconst volatile修饰符去掉后的类型,因为public后不能跟这样的修饰符,该类里没有虚函数

struct d1 : public ncvT

{

d1();

~d1() // throw();

char padding[256];

};

struct d2 : public ncvT // d2中加入一个虚函数

{

d2();

//加入一个虚函数,如果ncvT为非多态则会导致vptr的加入从而多占用4字节

virtual ~d2() // throw();

char padding[256];

};

// 如果T为多态类则valuetrue

static const bool value =

(sizeof(d2) == sizeof(d1));

};

// T并非类时采用这个版本

template <class T>

struct is_polymorphic_imp2

{

// 既然T不是类,那么就不存在多态,所以总是false

static const bool value = false;

};

// 这个selector根据is_class的真假来选择判断的方式

template <bool is_class>

struct is_polymorphic_selector

{

// 如果is_classfalse则由is_polymorphic_imp2来判断,这将导致结果总是false

template <class T>

struct rebind

{

typedef is_polymorphic_imp2<T> type; // 使用_imp2

};

};

//is_classtrue时使用该特化版本

template <>

struct is_polymorphic_selector<true> // #7

{

// 如果is_classtrue,则由is_polymorphic_imp1<>来作判断

template <class T>

struct rebind

{

typedef is_polymorphic_imp1<T> type; // 使用_imp1

};

};

// is_polymorphic完全由它实现

template <class T>

struct is_polymorphic_imp

{

// 选择selector

typedef

is_polymorphic_selector<is_class<T>::value> selector; // #6

typedef typename selector::template rebind<T> binder; // #8

typedef typename binder::type imp_type; // #9

static const bool value = imp_type::value;

};

注解

#6处如果T为类,则is_class<T>::valuetrue,则那一行实际上就是:

typedef is_polymorphic_selector<true> selector;

这将决议为is_polymorphic_selector的第二个重载版本#7,其中的template rebind将判断的任务交给is_polymorphic_imp1,所以#8行的binder其实就是is_polymorphic_selector<true>::rebind<T>。而#9行的imp_type其实就是is_polymorphic_imp1<T>结果正如预期。如果T不是类,按照类似的推导过程,最终会推导至is_polymorphic_imp2<T>::value,这正是false

嗨!这太烦琐了!你抱怨道:可以简化!。我知道,你可能会想到使用boost::ct_if(ct_if?:三元操作符的编译期版本,像这样使用:

typedef

ct_if<CompileTimeBool,TypeIfTrue,TypeIfFalse>::value

result;

则当CompileTimeBooltrueresultTypeIfTrue,否则resultTypeIfFalsect_if<>的实现很简单,模板偏特化而已)。于是你这样写:

typedef typename boost::ct_if<

is_class<T>::value,

is_polymorphic_imp1<T>,

is_polymorphic_imp2<T>,

>::type

imp_type;

static const bool value = imp_type::value;

这在我的VC7.0环境下的确编译通过并正常工作,但是有一个小问题:假如T不是class,比如,T是一个int,则编译器的类型推导会将is_polymorphic_imp1<int>赋给ct_if的第二个模板参数,在这个过程中编译器会不会实例化is_polymorphic_imp1<int>(或者,换句话说,编译器会不会去查看它的定义)呢?如果实例化了,那么其内部的struct d1 : public ncvT会不会也跟着实例化为struct d1:public int,如果是这样,那么将会有编译期错误,因为C++标准不允许有public int这样的东西出现。事实上我的编译器没有报错,即是说它并没有去查看is_polymorphic_imp1<int>的定义。

C++标准实际上也支持这种做法。但boost库中的做法更为保险,也许是为了应付一些老旧的编译器。

类型属性traits还有:

alignment_of is_const is_volatile is_pod has_trivial_constructor

类型间关系

is_base_and_derived(boost/type_traits/is_base_and_derived.hpp)

定义

template<typename B, typename D>

struct bd_helper

{

template<typename T>

static type_traits::yes_type check(D const volatile *, T);

static type_traits::no_type check(B const volatile *, int);

};

template<typename B, typename D>

struct is_base_and_derived_impl2

{

struct Host

{

// 该转换操作符当对象为const对象时才起作用

operator B const volatile *() const;

operator D const volatile *();

};

static const bool value =

sizeof(bd_helper<B,D>::check(Host(), 0)) // #10

== sizeof(type_traits::yes_type);

};

以上就是is_base_and_derived的底层机制。下面我就为你讲解它所仰赖的机制,假设有这样的类继承体系:

struct B {};

struct B1 : B {};

struct B2 : B {};

struct D : private B1, private B2 {};

D*转换为B1*会导致访问违规,因为私有基类部分无法访问,但是后面解释了这为什么不会发生。

首先来看一些术语:

SC - Standard Conversion

UDC - User-Defined Conversion

一个user-defined转换序列由一个SC后跟一个UDC后再跟一个SC组成。其中头尾两个SC都可以为到自身的转换(如:D->D)#10处将一个缺省构造的Host()交给bd_helper<B,D>::check函数。

对于static no_type check(B const volatile *, int)我们有如下可行的隐式转换序列:

Host -> Host const -> B const volatile* (UDC)

Host -> D const volatile* (UDC) -> B1 const volatile* / B2 const volatile* -> B const volatile* (SC)

而对于static yes_type check(D const volatile *, T),我们则有如下转换序列:

Host -> D const volatile* (UDC)

C++标准说,在重载决议中选择最佳匹配函数时,只考虑标准转换(SC)序列,而这个序列直到遇到一个UDC为止,对于第一个函数,将Host -> Host constHost -> Host比较,显然选择后者。因为后者是前者的一个真子集。因此,去掉第一个转换序列我们得到:

C -> D const volatile* (UDC) -> B1 const volatile* / B2 const volatile* -> B const volatile* (SC)

vs.

C -> D const volatile* (UDC)

这里采用选择最短序列的原则,选择后者,这表明编译器甚至根本不需要去考虑向B转换的多重路径,或者访问限制,所以转换二义性和访问违规也就不会发生。结论是如果D继承自B,则选择yes_type check()

如果D不是继承自B,则对于static no_type check(B const volatile *, int)编译器的给出的转换为:

C -> C const -> B const volatile*(UDC)

对于static yes_type check(D const volatile *, T)编译器给出:

C -> D const volatile* (UDC)

这两个都不错(都需要一个UDC),然而由于static no_type check(B const volatile *, int)为非模板函数,所以被编译器选用。结论是如果D并非继承自B,则选择no_type check()

另外,在我的VC7.0环境下,如果将Hostoperator B const volatile *() constconst拿掉,则结果将总是false

可惜这样的理解并不属于我,它们来自boost源代码中的注释。

is_convertible(boost/type_traits/is_convertible.hpp)

定义

template< typename From >

struct does_conversion_exist

{

template< typename To >

struct result_

{

// 当不存在从FromTo的任何转型时调用它

static no_type _m_check(...);

// 只要转型存在就调用它

static yes_type _m_check(To);

// 这只是个声明,所以并不占用空间,且没有开销。

static From _m_from;

enum

{

value =

sizeof( _m_check(_m_from) ) == sizeof(yes_type);

};

};

};

// 这是个为void准备的特化版本,因为不能声明void _m_from,只有void可以向void“转换

template<>

struct does_conversion_exist<void>

{

template< typename To >

struct result_

{

enum { value = ::boost::is_void<To>::value };

};

};

// is_convertible完全使用does_conversion_exist作底层机制,所以略去。

注解

does_conversion_exist也使用了与is_class_impl一样的技术。所以注解从略。该技术最初由Andrei Alexandrescu发明。

最后,Transformations Between Types(类型间转换)Synthesizing Types(类型合成)Function Traits(函数traits)的机制较为单纯,请自行参考boost提供的文档或头文件。

traits是泛型世界中的精灵:小巧,精致。traits也是泛型编程中最精微的东西,它们往往仰赖于一些编译期决议的规则,C++标准,和神奇的模板偏特化。这也导致了它们在不同的平台上可能有不同表现,更常见的是,在某些平台上根本无法工作。然而,由于它们的依据是C++标准,而编译器会越来越符合标准,所以这些问题只是暂时的。traits也是构建泛型世界的基本组件之一,它们往往能使设计变得优雅,精致,甚至完美。

目录(展开boost源码剖析》系列文章)

分享到:
评论

相关推荐

    C++ lua Kaguya 应用

    C++ lua Kaguya 应用

    stl_algobase.h

    #ifndef __TYPE_TRAITS_H #include &lt;type_traits.h&gt; #endif #include #include #include #include #include #ifdef __STL_USE_NEW_IOSTREAMS #include #else /* __STL_USE_NEW_IOSTREAMS */ #include #...

    callable_traits, callable类型的现代 C 型特征和泛函.zip

    callable_traits, callable类型的现代 C 型特征和泛函 Boost.CallableTraits CallableTraits是一个C 11头库,用于检查。合成和分解可以调用类型。这里有最新的文档,这里是 。在CallableTraits中,被正式地检查过,...

    STL.源码剖析_____________

    这本书不适合C++ 初学者,不适合 Genericity(泛型技术)初学者,或 STL 初学者。这本书也不适合带领你学习面向对象(Object Oriented)技术 — 是的,STL 与面向对象没有太多关连。本书前言清楚说明了书籍的定位和...

    Python库 | link_traits-1.0.3.tar.gz

    资源分类:Python库 所属语言:Python 资源全名:link_traits-1.0.3.tar.gz 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059

    c++_traits.pdf

    c++_traits Classes which encapsulate properties of types Is this an integer type? An arithmetic type? A pointer type?... Closely related to partial specialization

    哈希表(带template)

    return (::_Fnv1a_append_bytes(::_FNV_offset_basis, reinterpret_cast*&gt;(_First), _Count * sizeof(_Kty))); } /*hash_val(string)*/ template&lt;class _Elem, class _Traits, class _Alloc&gt; inline size_t...

    定长字符串模板类

    typename T, size_t MaxSize, typename Traits=std::char_traits&lt;;T&gt;; &gt;; class basic_small_string;; // : public basic_small_string_base&lt;;T,Traits&gt;; 特定长度的定长字符串模板类.如果C++...

    R Material_traits.html

    R Material_traits.html

    escomplex-traits:未维护

    复杂特征 :red_exclamation_mark: :anguished_face: :red_exclamation_mark: :anguished_face: :red_exclamation_mark: :anguished_face: :red_exclamation_mark: :anguished_face: :red_exclamation_mark: :...

    variant_access:https:docs.rsvariant_access_traits

    变体访问 一组特征和宏,用于基于C ++标准库中的std :: variant API为Rust枚举定义通用api基本用法考虑以下枚举: enum Enum { F1 ( i32 ), F2 ( bool )} 当然,我们可以直接使用字段名称和match语句使用此类枚举。...

    ft_containers:列表,向量,地图(基于红黑树),堆栈和队列STL容器的实现。 此外,重新实现了默认分配器,reverse_iterator,iterator_traits,一些type_traits,pair和几个关系运算符模板。 参考标准C ++ 98

    ft_containers 42 ft_containers项目关于一些C ++ 98的STL容器 ... iterator_traits和一些type_traits改编 对和一些关系运算符模板 运行测试 clang++ main.cpp -std=c++98 -Wall -Wextra -Werror && ./a.out

    STL 源码剖析(侯捷先生译著)

    3.7 SGI STL的私房菜:__type_traits 103 第4章 序列式容器(sequence containers) 113 4.1 容器概观与分类 113 4.1.1 序列式容器(sequence containers) 114 4.2 vector 115 4.2.1 vector 概述 115 4.2.2 ...

    STL源码剖析.pdg

    3.7 sgi stl的私房菜:__type_traits 103 第4章 序列式容器(sequence containers) 113 4.1 容器概观与分类 113 4.1.1 序列式容器(sequence containers) 114 4.2 vector 115 4.2.1 vector 概述 115 4.2.2 ...

    ASP.NET、SharePoint中另存文件的长文件名被截断的原因及解决办法

    个问题起初发生在SharePoint的环境中,我以为是SharePoint限制了长度,后来我试验了一下,在ASP.NET的应用中也同样会发生。

    boost 1.41 中文文档,使用帮助,教程手册

    fatalerror99 array, bind & mem_fn, dynamic_bitset, function, functional/hash, in_place_factory & typed_in_place_factory, lambda, ref, smart_ptr, static_assert, string_algo, type_traits, typeof ...

    cxl:一些C ++实用程序

    function_traits包括arity , result_type和arg&lt;N&gt; ,还定义了arguments_tuple ,其中包含所有参数类型 make_function创建与原始函数签名匹配的std :: function包装器 make_function_type是std :: function包装器的...

    STL源码剖析 电子版

    侯捷 版本 STL源码剖析 C++ 学习不二经典 学习编程的人都知道,阅读、剖析名家代码乃是提高水平的捷径。源码之前,了无秘密。大师们的缜密思维、经验结晶、技术思路、独到风格,都原原本本体现在源码之中。 这本书...

    现代C ++ 中的模板元编程 (TMP) 艺术_C++_代码_下载

    Monster 是一个元编程库,它只有头文件,可扩展且面向现代 C++。 它展示了一种编译时算法、序列和高阶元函数的纯类型编程形式。 Monster 提供了一个概念基础和一套广泛而...&lt;type_traits&gt; 标头中的所有 C++20 类型特征

    STL源码剖析_Table_stlmemory_c++prim_vector_

    STL源码剖析,这本书所呈现的源码,使读者看到vector的实现、list的实现、heap的实现、deque的实现、Red Black tree的实现、hash table的实现、set/map的实现;看到各种算法(排序、查找、排列组合、数据移动与复制...

Global site tag (gtag.js) - Google Analytics