很早之前,忘了是看书还是别人介绍了,说STL里有一些类型检查的东西,比如vector里面会检查是否定义了赋值操作符,还有的检查是否有默认参数的构造函数之类的。看STL源码的时候发现了一个concept_check的头文件,发现其中的内容还真是不少。有一些就是做这种检查的。
STL默认提供了很多种操作的检查,比如:
_Allocator
_Assignable
_DefaultConstructible
_EqualityComparable
_LessThanComparable
_TrivialIterator
_InputIterator
_OutputIterator
_ForwardIterator
_BidirectionalIterator
_RandomAccessIterator
_Mutable_TrivialIterator
_Mutable_ForwardIterator
_Mutable_BidirectionalIterator
_Mutable_RandomAccessIterator
现在来看看是怎么实现的。
举个例子,一个C++的类TesetClass,如果我没有重写operator=,默认是由编译器提供一个。而如果我写了这个函数,但是没有实现,并且把它写为private,这也算是一个常用的手法。这种情况下,如何去检查出来?很简单,如果我们定义它的一个对象a1,再定义一个对象a2,如果调用a1=a2,编译器就会报错,提示我们赋值操作符为私有,没有实现。很好,STL中也是采用类似的方式。来看下它是怎么做的。
在此,也以赋值操作符为例。
在vector中可以看到如下的一句代码:
__STL_CLASS_REQUIRES(_Tp, _Assignable);
找到宏定义:
#define __STL_CLASS_REQUIRES(__type_var, __concept) typedef void (* __func##__type_var##__concept)( __type_var ); template <__func##__type_var##__concept _Tp1> struct __dummy_struct_##__type_var##__concept { }; static __dummy_struct_##__type_var##__concept< __concept##_concept_specification< __type_var>::__concept##_requirement_violation> __dummy_ptr_##__type_var##__concept
分析一下,typedef void (* __func##__type_var##__concept)( __type_var ); 定义了一个函数指针,参数是__type_var。
template <__func##__type_var##__concept _Tp1> struct __dummy_struct_##__type_var##__concept { };
上面两句定义了一个模板类,不要小看它,可是大有作为的。它的模板参数呢,就是上面定义的函数指针
static __dummy_struct_##__type_var##__concept< __concept##_concept_specification< __type_var>::__concept##_requirement_violation> __dummy_ptr_##__type_var##__concept
定义了前面提到的模板类的一个对象。这个模板对象的模板实参呢,就是下面即将提到的,STL定义好的函数,而这个函数就是用于检查赋值操作符的。这句中__concept##_concept_specification< __type_var>::__concept##_requirement_violation整体是模板实参,也就是一个函数指针,而这个模板实参又是用的一个模板。__concept##_requirement_violation函数,是模板类__concept##_concept_specification< __type_var>的一个函数。下面我们继续上代码,现在越来越清晰了。
代入_Assignable,来看下。
template <class _Type> struct _Assignable_concept_specification { static void _Assignable_requirement_violation(_Type __a) { _STL_ERROR::__assignment_operator_requirement_violation(__a); _STL_ERROR::__copy_constructor_requirement_violation(__a); _STL_ERROR::__const_parameter_required_for_copy_constructor(__a,__a); _STL_ERROR::__const_parameter_required_for_assignment_operator(__a,__a); } };
模板类_Assignable_concept_specification其实就是上面的__concept##_concept_specification< __type_var>,它的函数_Assignable_requirement_violation其实就是__concept##_requirement_violation了。现在都已经清楚了。但是具体内容还没有,别着急,谜底马上揭晓。
struct _STL_ERROR { template <class _Type> static _Type __assignment_operator_requirement_violation(_Type __a) { __a = __a; return __a; } }
这里就值给出了一个函数的实现,不过已经能够充分说明问题了。这个函数调用了赋值操作!还记得我们是怎么判断赋值操作的吧,就是这样子,是不是很简单?
一个静态模板对象传递一个模板类型的函数,这个函数调用会检查赋值操作,当然不止是赋值操作,文章最开始提到的几种操作都包含。
不过得说一下一个令人沮丧的结果,我在vc2010中采用如是方式进行验证,发现编译器并不会报错,然而使用g++编译时则会报错。看来不同的编译器是做了不同的处理的。