很早之前,忘了是看書還是別人介紹了,說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++編譯時則會報錯。看來不同的編譯器是做了不同的處理的。