STL concept check

什麼時機下最合適選擇那種容器,依據:

1 元素多寡

2 元素構造複雜度

3 元素存取行爲特性

 
Concept Check in STL
 

轉載出處:http://hi.baidu.com/walkandsing

Matt Austern在Generic Programming and the STL中提出了Concept、Model、Refinement的概念,實際上是換湯不換藥,就是類似於Class、Object、SubClass的概念,只不過是在Template的語義下面解釋的而已,唯一的差別就是在Template下,類型是implicit的,對類型的要求比較鬆散。而所謂的Concept Checks就是類似於assert的東西,專業點講就是static assertion,就是利用編譯器在Compiling的時候的檢查來做一些檢查,例如:

#define STATIC_ASSERTION(_cond,_name) typedef char STATIC_ASSERT_FAILED_ ## _name[(_cond)?1:-1]
這裏_cond必須是編譯期間能判定的結果,若_cond爲真,上式相當於定義了一個一維char數組類型,反之,數組的維度爲-1,不能通過編譯,這時數組類型名包含了錯誤信息。
一個典型的應用就是判斷類型的字節數。可定義如下宏:
#define STATIC_SIZE_ASSERT(_type,_size) STATIC_ASSERT ( sizeof(_type) == _size, _type##_MUST_BE_##_size##_BYTES)

STL中和概念檢查有關的文件有:concept_checks.h、container_concepts.h、sequence_concepts.h和assoc_container_concepts.h。

先從concept_checks.h開始,這個文件定義了一些基礎的概念和iterator的概念,主要的接口是:

#define __STL_CLASS_REQUIRES(__type_var, __concept)

檢查__type_var是否滿足__concept,當前定義的概念有:_Allocator_Assignable_DefaultConstructible_EqualityComparable_LessThanComparable_TrivialIterator_InputIterator_OutputIterator_ForwardIterator_BidirectionalIterator_RandomAccessIterator_Mutable_TrivialIterator_Mutable_ForwardIterator_Mutable_BidirectionalIterator_Mutable_RandomAccessIterator

#define __STL_CONVERTIBLE(__type_x, __type_y)

檢查類型_x能否轉換爲類型_y

#define __STL_CLASS_REQUIRES_SAME_TYPE(__type_x, __type_y)

檢查類型_x能否和類型_y爲同一個類型。

#define __STL_CLASS_GENERATOR_CHECK(__func, __ret)

用於在算法generategenerate_n中用來檢查generator合法性。

#define __STL_CLASS_UNARY_FUNCTION_CHECK(__func, __ret, __arg)

檢查__func的參數爲__arg類型,返回值爲__ret類型。

#define __STL_CLASS_BINARY_FUNCTION_CHECK(__func, __ret, __first, __second)

檢查__func的參數爲__first__second類型,返回值爲__ret類型。

#define __STL_CLASS_REQUIRES_BINARY_OP(__opname, __ret, __first, __second)

檢查__ret __opname(__first, __second)是否合法。

 

在看這些接口的具體實現前,還需要了解一點他們的實現細節,在concept_checks.h中定義了一個類_STL_ERROR,裏面定義了一些static模板函數,這些方法組成了static assertion的最小單元:

template <class _Type>

static _Type

__default_constructor_requirement_violation(_Type) {

    return _Type();

}

template <class _Type>

static _Type

__assignment_operator_requirement_violation(_Type __a) {

    __a = __a;

    return __a;

}

….(省略)

這些方法都是利用一些簡單的表達式使得編譯器來判斷__Type是否支持某一種操作,當其不支持的時候,compiler就會報錯,函數名將被打印出來,這樣用戶就能知道出錯的原因了。還有一些模板方法是用來檢測類型裏是否定義了某些typedef,如:

template <class _Iter>

struct __value_type_type_definition_requirement_violation {

typedef typename __STD::iterator_traits<_Iter>::value_type value_type;

};

 

template <class _Iter>

struct __difference_type_type_definition_requirement_violation {

typedef typename __STD::iterator_traits<_Iter>::difference_type

          difference_type;

};

….

然後基於這些atomic的static assertion組合出了一些概念:

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);

}

};

template <class _TrivialIterator>

struct _TrivialIterator_concept_specification {

static void

_TrivialIterator_requirement_violation(_TrivialIterator __i) {

typedef typename

    __value_type_type_definition_requirement_violation<_TrivialIterator>::

    value_type __T;

// Refinement of Assignable

_Assignable_concept_specification<_TrivialIterator>::

    _Assignable_requirement_violation(__i);

// Refinement of DefaultConstructible

_DefaultConstructible_concept_specification<_TrivialIterator>::

    _DefaultConstructible_requirement_violation(__i);

// Refinement of EqualityComparable

_EqualityComparable_concept_specification<_TrivialIterator>::

    _EqualityComparable_requirement_violation(__i);

// Valid Expressions

_STL_ERROR::__dereference_operator_requirement_violation(__i);

}

};

template <class _InputIterator>

struct _InputIterator_concept_specification {

static void

_InputIterator_requirement_violation(_InputIterator __i) {

// Refinement of TrivialIterator

_TrivialIterator_concept_specification<_InputIterator>::

    _TrivialIterator_requirement_violation(__i);

// Associated Types

__difference_type_type_definition_requirement_violation<_InputIterator>();

__reference_type_definition_requirement_violation<_InputIterator>();

__pointer_type_definition_requirement_violation<_InputIterator>();

__iterator_category_type_definition_requirement_violation<_InputIterator>();

// Valid Expressions

_STL_ERROR::__preincrement_operator_requirement_violation(__i);

_STL_ERROR::__postincrement_operator_requirement_violation(__i);

}

};

….

到目前爲止,這些函數都是些函數調用,怎麼達到在編譯的時候判斷的目的呢?謎底就在接口定義中,以__STL_REQUIRES爲例,它的定義爲:

#define __STL_REQUIRES(__type_var, __concept) /

do { /

void (*__x)( __type_var ) = __concept##_concept_specification< __type_var >/

::__concept##_requirement_violation; __x = __x; } while (0)

 

比如在很多算法的開頭都有檢查:

__STL_REQUIRES(_InputIter, _InputIterator);

 

評論:定義函數指針不會觸發模板實例化,指針賦值使編譯器會靜態檢查模板內部實現。

這時相當於block裏定義了一個函數指針__x,而__x=__x將觸發模板的實例化,這時編譯器開始對_InputIterator_concept_specification::_InputIterator_requirement_violation中的所有的static assertion進行檢查,但又不會調用這些函數,這樣只會在編譯期間會產生開銷。

其他的接口定義也都類似,值得一提的還有__STL_CONVERTIBLE和__STL_REQUIRES_SAME_TYPE,實際上在Modern C++ Design中有提到這兩個模板,但我發現在STL中這兩個模板的實現更加簡潔。

template <class _TypeX, class _TypeY>

struct _STL_CONVERT_ERROR {

static void

__type_X_is_not_convertible_to_type_Y(_TypeX __x, _TypeY) {

    _TypeY __y = __x;

    __sink_unused_warning(__y);

}

};

#define __STL_CONVERTIBLE(__type_x, __type_y) /

do { /

void (*__x)( __type_x , __type_y ) = _STL_CONVERT_ERROR< __type_x , /

__type_y >::__type_X_is_not_convertible_to_type_Y; /

__x = __x; } while (0)

很簡單,就是通過賦值來判斷的。

template <class _Type> struct __check_equal { };

 

template <class _TypeX, class _TypeY>

struct _STL_SAME_TYPE_ERROR {

static void

__type_X_not_same_as_type_Y(_TypeX , _TypeY ) {

    __check_equal<_TypeX> t1 = __check_equal<_TypeY>();

}

};

#define __STL_REQUIRES_SAME_TYPE(__type_x, __type_y) /

do { /

void (*__x)( __type_x , __type_y ) = _STL_SAME_TYPE_ERROR< __type_x, /

    __type_y >::__type_X_not_same_as_type_Y; /

__x = __x; } while (0)

這個稍微麻煩一點,雖然也是通過賦值來判斷但引入了一個空模板__check_equal,當_TypeX和_TypeY類型不同的時候,__check_equal<_TypeX>和__check_equal<_TypeY>肯定不同,即使_TypeX和_TypeY是convertiable的,__check_equal<_TypeX>和__check_equal<_TypeY>的賦值也是非法的。

在container_concepts.h和sequence_concepts.h中的方法和上述類似,在此不再累述。

發佈了46 篇原創文章 · 獲贊 4 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章