青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

在C++中偵測內(nèi)嵌類型的存在

Posted on 2008-06-04 15:23 RichardHe 閱讀(1142) 評論(0)  編輯 收藏 引用 所屬分類: [轉(zhuǎn)]
現(xiàn)在學(xué)校里假如不止學(xué)生教師,還有工人,警衛(wèi)等其它人員。如果他們不會在類內(nèi)部typedef任何東西,則Register需要一種機制以確定T內(nèi)部是否typedef了某個標(biāo)識符(例如person_tag)。如果沒有,就默認(rèn)處理。如果有,則再進行更詳細(xì)的分類。

動機(Motivation)

假設(shè)一所大學(xué)的注冊系統(tǒng)提供了一個注冊函數(shù):

 

template<class T>

void Register(T person)

{

Register(person, typename T::person_tag());

};

 

而對于注冊者有以下幾種標(biāo)識:

 

struct student_tag{};

struct teacher_tag{};

 

還有Register的幾個供內(nèi)部使用的重載版本:

 

template<class T> void Register(T p, student_tag){...} // 注冊學(xué)生

template<class T> void Register(T p, teacher_tag){...} // 注冊教師

 

并規(guī)定學(xué)生類一定要在內(nèi)部typedef student_tag person_tag教師類typedef teacher_tag person_tag這樣,當(dāng)傳給起初的那個Register的對象為學(xué)生類對象時typename T::person_tag()其實就構(gòu)造了一個student_tag對象,從而激發(fā)函數(shù)重載,調(diào)用Register內(nèi)部版本的template<class T> void Register(T p, student_tag)版本。其他情況亦均有對應(yīng)。這是泛型編程里的常用手法(靜態(tài)多態(tài)),STL里屢見不鮮。

 

問題是,現(xiàn)在學(xué)校里假如不止學(xué)生教師,還有工人,警衛(wèi)等其它人員。如果他們不會在類內(nèi)部typedef任何東西,則Register需要一種機制以確定T內(nèi)部是否typedef了某個標(biāo)識符例如person_tag。如果沒有,就默認(rèn)處理。如果有,則再進行更詳細(xì)的分類。

 

實現(xiàn)(Implementation)

這個問題可能有兩個實現(xiàn)途徑。

 

一是利用函數(shù)重載,具體如下:

 

typedef char (&yes_type)[1]; // sizeof(yes_type)==1

typedef char (&no_type)[2]; // sizeof(no_type)==2

 

以上的兩個typedef用于識別不同的重載函數(shù)。char (&)[1]表示對char[1]數(shù)組的引用,所以sizeof(char(&)[1])==sizeof(char[1])==1注意圍繞&符號的一對圓括號,它們是必要的,如果沒有將會導(dǎo)致編譯錯誤,正如char* [1]將被解析為char*的數(shù)組,char& [1]將被解析為引用的數(shù)組,而后者是非法的。將&用圓括號包圍則改變了運算符的結(jié)合優(yōu)先序,這將被解析為對char[1]數(shù)組的引用。

 

template<class T>

struct does_sometypedef_exists

{

template<class U>

static yes_type check(U, typename U::key_type* =0); // #1

static no_type check(...);

static T t;   // 聲明

static const bool value = sizeof(check(t))==sizeof(yes_type);

};

 

注意,#1處,*=之間的空格是必要的否則編譯器會將它解析為operator*=操作符。

 

在我的VC7.0環(huán)境下,以下測試是成功的:

 

struct A{};

struct B

{

typedef int key_type;

};

int main()

{

std::cout << does_sometypedef_exists<A>::value<<' '  // 0

<< does_sometypedef_exists<B>::value<<' '  // 1

<< std::endl;

};

 

下面我為你講解它的原理。

 

當(dāng)進行重載解析時,編譯器會首先嘗試實例化可以匹配的模板函數(shù)并將它們納入到有待進行重載解析的函數(shù)的候選單之列,在本例中,當(dāng)typename T::key_type不存在時,check的第一個模板版本不能實例化因為其第二個參數(shù)類型typename U::key_type*不存在,所以只能匹配第二個版本。當(dāng)typename T::key_type存在時,第一個模板函數(shù)可以實例化,且可以匹配注意第二個參數(shù)為缺省參數(shù),所以無疑編譯器會匹配第一個版本,因為C++標(biāo)準(zhǔn)保證:只有當(dāng)其它所有重載版本都不能匹配的時候含有任意類型參數(shù)列表的版本在本例中那是no_type check(...)才會被匹配。

 

一個值得注意的地方是check的第一個版本只能是模板函數(shù),因為當(dāng)編譯器推導(dǎo)類型的過程中發(fā)現(xiàn)該模板函數(shù)不能實例化時它就不去實例化它,而不是產(chǎn)生編譯錯誤除非沒有其它可匹配的重載版本。因為編譯錯誤只有將代碼編譯的過程中才會產(chǎn)生,而既然模板沒有實例化,那么該模板實際上并沒有經(jīng)過編譯。

 

然而,如果它不是模板函數(shù),則隨著does_sometypedef_exists類的實例化。它也會被實例化,然而如果不存在T::key_type,那么,該函數(shù)就成為非法。

 

還有一個值得注意的地方是:does_sometypedef_exists內(nèi)部的static T t;只是一個聲明,并不占用內(nèi)存空間,更妙的是,因為是個聲明,所以編譯器根本不會對它初始化,所以它的默認(rèn)構(gòu)造函數(shù)就根本不會被執(zhí)行,事實上,編譯器在這種情況下甚至不會去看一看它是否有可用的默認(rèn)構(gòu)造函數(shù),它只需要類型信息就足夠了,不是么?因此,即使由于某些原因例如,想讓T從堆上創(chuàng)建T的默認(rèn)構(gòu)造函數(shù)被禁止設(shè)為private,那么以上的traits也不會通不過編譯。但是,等等!你仿佛意識到了問題:“check的參數(shù)是傳值的!這時如果T的拷貝構(gòu)造函數(shù)是私有的將會發(fā)生什么事情呢?事實是,根本不用去擔(dān)心,sizeof的世界里,根本不會發(fā)生求值行為,編譯器只需要有關(guān)類型的信息。在編譯器內(nèi)部蘊涵有一個巨大的類型推導(dǎo)系統(tǒng)。無論sizeof(...)里的表達(dá)式多么復(fù)雜,其類型都會最終在編譯期被正確推導(dǎo)出來。而對于sizeof(check(t)),編譯器有了函數(shù)的返回值類型信息就夠了,它并不會去執(zhí)行函數(shù)的代碼,也不會做實際的傳參行為,所以拷貝構(gòu)造也就無從發(fā)生。

 

但這里有一個十分怪異的問題在我的VC7.0環(huán)境下存在,假設(shè)我們增加一個新類:

 

struct C

{

template<class T>

struct key_type{};  // 請注意這是個模板類

};

 

按理說,這種情況下does_sometypedef_exists<C>::value應(yīng)該為false,因為第一個重載版本的typename U::key_type*不能被推導(dǎo)為C::key_type* C::key_type是個模板,它需要模板參數(shù)來實例化,然而在我的VC7.0下它通過編譯了,并且結(jié)果為true就是說重載解析為第一個check函數(shù))。如果我將check的第一個版本作一點小小的改動,像這樣:

 

template<class U>

static yes_type check(U,

typename U::key_type* = (typename U::key_type*)0);

 

我僅僅加了一個轉(zhuǎn)換,編譯器就開始抱怨說使用模板類它指的是C::key_type需要模板參數(shù)了。我作了另外的種種測試甚至我發(fā)現(xiàn)如果將10傳給它的第二個參數(shù),編譯器會說不能將int轉(zhuǎn)換為C::key_typ*是的,這是編譯錯誤的原文,這是否表示編譯器承認(rèn)C::key_type*為一種類型呢?我不知道)。結(jié)論是只有當(dāng)typename U::key_type*作為模板函數(shù)的參數(shù)類型時這種情況才會發(fā)生。

 

第二種實現(xiàn)是利用模板偏特化及默認(rèn)模板參數(shù)的規(guī)則

 

template<class T,class>

struct check_helper

{

typedef T type;

};

 

template<class T,class =T>

struct does_sometypedef_exists_1

{

static const bool value=false;

};

 

template<class T>

struct does_sometypedef_exists_1<T,

typename check_helper<T, typename T::key_type>::type>

{

static const bool value=true;

};

 

這看起來很小巧,僅僅使用了模板偏特化。但是請耐心聽我解釋。

 

如果typename X::key_type存在假設(shè)X為任意類,則does_sometypedef_exists_1<X>首先由模板推導(dǎo)將does_sometypedef_exists_1的模板參數(shù)T匹配為X,則其偏特化版本因而被推導(dǎo)為:

 

struct does_sometypedef_exists_1<X,

typename check_helper<X,typename X::key_type>::type>

 

typename check_helper<X,typename X::key_type>::type根據(jù)check_helper的定義其實就是X所以該偏特化版本其實被推導(dǎo)為:

 

struct does_sometypedef_exists_1<X,X>

 

所以,如果你這樣測試:does_sometypedef_exists_1<X>::value,根據(jù)does_sometypedef_exists_1缺省定義第二個模板參數(shù)默認(rèn)為T,你寫的相當(dāng)于:does_sometypedef_exists_1<X, X>::value

 

而根據(jù)上面的推導(dǎo),如果typename X::key_type存在,則does_sometypedef_exists_1的偏特化版本也存在且形式為

 

struct does_sometypedef_exists_1<X, X>

 

于是編譯器選擇匹配偏特化版本,其中的value值為true

 

而如果typename X::key_type不存在,typename check_helper<X, typename X::key_type>::type也就隨之不存在,則does_sometypedef_exists_1的偏特化版本也就隨之不存在,于是編譯器會選擇使用缺省定義,其中value值為false。這正是我們所想要的結(jié)果。

 

測試(Test)

現(xiàn)在對我們的兩個實現(xiàn)版本測試一下吧,假設(shè)有一下幾個類:

 

// 沒有key_type

struct A{};

 

// typedef

struct B{typedef int key_type;};

 

// key_type為成員函數(shù)

struct C{void key_type(void){}};

 

// key_type為靜態(tài)常量數(shù)據(jù)成員

struct D{static const bool key_type=false;};

 

// 定義,D里面的是聲明

const bool D::key_type;

 

// key_type為模板類

struct E{

template<class>

struct key_type{};

};

 

template<class T>

struct does_typedef_exists

{

typedef does_sometypedef_exists<T> impl_type;

static const bool value = impl_type::value;

};

 

int main()

{

std::cout << does_typedef_exists<A>::value<<' '

<< does_typedef_exists<B>::value<<' '

<< does_typedef_exists<C>::value<<' '

<< does_typedef_exists<D>::value<<' '

<< does_typedef_exists<E>::value<<' '

<< std::endl;

return 0;

};

 

在我的VC7.0編譯平臺上:

 

如果使用第一種實現(xiàn),這將輸出:0 1 0 0 1

如果使用第二種實現(xiàn),這將輸出:0 1 0 0 0

 

很顯然,兩種實現(xiàn)對于struct E給出的結(jié)果不一樣。事實上,我們希望該traitsE這種情況給出的結(jié)果為1。從這一點講第一種實現(xiàn)在我的編譯器上已經(jīng)神差鬼使的成功了,而第二種實現(xiàn)還沒有。不管怎樣,我們都必須試圖找到一種方法來實現(xiàn)它。這種方法不可以像實現(xiàn)一那樣依賴與編譯器的可能的一時糊涂,它應(yīng)該是以C++標(biāo)準(zhǔn)的規(guī)則為依據(jù)的。Paul Mensonides提供了一種方法,然而在我的VC7.0上編譯不能通過。后面我會介紹它。

 

改進(Improvement)

第一種實現(xiàn)還可以做一點改進,像這樣:

 

template<class T>

struct does_sometypedef_exists

{

template<class U>

static yes_type check(typename U::key_type* );

 

template<class U>

static no_type check(...);

 

static const bool value = sizeof(check<T>(0))==sizeof(yes_type);

};

 

這樣,去掉static T t,和check的第一個參數(shù),會使代碼看上去更簡潔和更可靠一些。

 

封裝(Encapsulation)

現(xiàn)在我們的traits只能偵測typename T::key_type的存在性,我們需要一個擴充的機制,以讓我們能夠偵測任意名稱的內(nèi)嵌類型的存在性。我們使用宏:

 

#define IMPLEMENT_TYPEDEF_EXISTS(id) \

template<class T> \

struct does_sometypedef_exists_##id \

{ \

private: \

template<class U> \

static yes_type check(typename U::id*); \

template<class U> \

static no_type check(...); \

public: \

static const bool value=sizeof(check<T>(0))==sizeof(yes_type); \

};

 

#define DOES_TYPEDEF_EXISTS(T,id) \

does_sometypedef_exists_##id<T>

 

經(jīng)過這重封裝,當(dāng)你要偵測某個名稱的內(nèi)嵌類型如some_type時,你先在任何函數(shù)之外寫這樣的代碼:

 

IMPLEMENT_TYPEDEF_EXISTS(some_type)

 

這將會擴展成一個名為does_sometypedef_exists_some_type的模板類,然后你這樣使用它:

 

DOES_TYPEDEF_EXISTS(X,some_type)::value;

 

這將偵測類X中有沒有some_type。不將::value直接納入到宏中的原因是為了保留traits編程的風(fēng)格。

 

Paul Mensonides對內(nèi)嵌template的偵測方法

Paul MensonidesBoost庫的preprocesser部分的設(shè)計者,那完全是一個宏的世界,也是Boost庫中的一個十分精巧的部分。我最初是在comp.lang.c++.moderated上看到他關(guān)于這個問題的解答的。

 

template<class> struct split;  // 缺省聲明,因為不會被匹配所以不用定義

 

// 以下是偏特化

template< template<class> class T, class T1 > // T為模板

struct split< T<T1> > {

struct type { };

};

 

template< template<class, class> class T, class T1, class T2 >

struct split< T<T1, T2> > {

struct type { };

};

 

// etc. :(,后面有支持更多模板參數(shù)的版本,從略

 

template<class T> class has_template_key_type

{

private:

template<class U>

static yes_type check(

typename split<

typename U::template key_type<null_t> >::type*

);

 

template<class U>

static yes_type check(

typename split<

typename U::template key_type<null_t, null_t> >::type*

);

 

// etc. :( 后面有支持更多模板參數(shù)的版本,從略

 

template<class U> static no_type check(...);

 

public:

static const bool value

            = sizeof(check<T>(0)) == sizeof(yes_type);

};

 

template<class T, bool V = has_template_key_type<T>::value>

class has_key_type

{

private:

template<class U> static yes_type check(typename U::key_type*);

template<class U> static no_type check(...);

 

public:

static const bool value

            = sizeof(check<T>(0)) == sizeof(yes_type);

};

 

template<class T> struct has_key_type<T, true>

{

static const bool value = false;

};

 

Paul Mensonides說它能夠工作,我也覺得根據(jù)標(biāo)準(zhǔn)它也該能夠工作,但事實是在我的VC7.0上編譯器有一大堆抱怨。我試了其它各種方法,結(jié)果總是類似的編譯錯誤將我擋住。我希望它在你的編譯器上能夠工作。

 

這里的原理是這樣的,如果類型X有內(nèi)嵌模板類型定義key_type,則has_template_key_type中的返回yes_type的那些成員函數(shù)總有一個能夠與它匹配,而其它則不會被實例化VC7.0仿佛總試圖將其它的也實例化了,結(jié)果它總會抱怨說模板參數(shù)太少或太多

 

然而Paul Mensonides的這個解決方案還有個問題:如果那個內(nèi)嵌的模板類的定義像如下這個樣子:

 

template<int>

struct key_type{};

 

則將沒有任何一個返回yes_type的重載版本能和它匹配,看看split類的定義吧,它的template template模板參數(shù)的形式是template<class[ ,class ,...]> class T,而上面的key_type的形式為template<int> class key_type它們無法匹配,如果試圖再加入一個能與其匹配的split偏特化版本

 

template<template<int>class T,int T1> struct split<T<T1> >{...};

 

這也是不實際的。因為intclass可能有無窮多種組合。如果key_type再變成template<int, class> class key_type呢?如果...,總之,如你所見,以int這類non-type parameter作為模板參數(shù)的加入使事情有了無限多種可能。split將窮于應(yīng)付。

 

結(jié)論(Conclusion)

對于最后我提出的問題,仿佛沒有一個好的解決方案。所以只能放棄這種內(nèi)嵌template的可能,假定情況是單純的。對于后者,這種技術(shù)有較好的表現(xiàn)。

來自:http://blog.csdn.net/pongba/archive/2004/08/24/82783.aspx

posts - 94, comments - 138, trackbacks - 0, articles - 94

Copyright © RichardHe

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美激情视频给我| 亚洲精品中文字幕女同| 蜜臀av国产精品久久久久| 国产精品一区二区三区乱码| 亚洲日韩视频| 伊人婷婷欧美激情| 久久久久久成人| 久久影院亚洲| 亚洲国产91色在线| 久久综合九色综合欧美就去吻 | 国产精品美女999| 亚洲欧美视频一区二区三区| 亚洲精品视频免费| 日韩亚洲综合在线| 欧美美女bbbb| 亚洲毛片播放| 亚洲一区二区三区影院| 欧美午夜片在线观看| 9久re热视频在线精品| 亚洲特级片在线| 国产精品理论片| 午夜宅男欧美| 免费欧美网站| 亚洲人成人99网站| 欧美日韩中文字幕精品| 亚洲欧美国产精品桃花| 久久中文字幕一区| 99视频一区二区| 国产精品视频福利| 久久精选视频| 亚洲日产国产精品| 亚洲欧美国产日韩天堂区| 国产午夜精品久久久久久久| 久久久久久亚洲综合影院红桃| 欧美顶级大胆免费视频| 亚洲香蕉伊综合在人在线视看| 国产精品综合| 免费欧美视频| 亚洲一区成人| 欧美韩日一区二区三区| 亚洲一区二区三区在线| 精品动漫3d一区二区三区免费版| 欧美成人精品在线观看| 亚洲一卡久久| 亚洲电影第三页| 亚洲欧美久久| 亚洲丰满在线| 国产精品欧美久久久久无广告| 欧美在线黄色| 亚洲精品国产欧美| 久久久久久亚洲精品杨幂换脸 | 亚洲视频一区在线观看| 国产欧美一二三区| 欧美xx视频| 欧美亚洲在线视频| 亚洲精品美女在线观看播放| 久久福利一区| 中日韩午夜理伦电影免费| 国产亚洲一区二区三区在线播放| 欧美阿v一级看视频| 亚洲欧美美女| 亚洲免费激情| 欧美第一黄色网| 欧美在线观看一区| 一区二区三区蜜桃网| 亚洲电影网站| 国内精品久久久久久久影视蜜臀 | 欧美日韩免费观看一区| 久久激情综合网| 亚洲天堂av电影| 亚洲国产精品嫩草影院| 久久久999精品| 亚洲一区尤物| 日韩一级片网址| 亚洲大胆美女视频| 国产一区二区视频在线观看| 国产精品久久77777| 欧美韩日一区二区三区| 久久久久久夜精品精品免费| 免费成人激情视频| 亚洲日本视频| 一区精品在线| 国产亚洲欧美另类一区二区三区| 欧美日韩免费观看一区三区| 免费高清在线一区| 久久成人国产| 欧美一区二区福利在线| 亚洲一卡二卡三卡四卡五卡| 日韩网站在线观看| 亚洲激情图片小说视频| 欧美激情五月| 亚洲福利电影| 亚洲国产精品久久久久秋霞影院| 美女精品在线观看| 老鸭窝亚洲一区二区三区| 久久国产精品黑丝| 久久成人免费视频| 久久精品综合网| 久久伊人精品天天| 久久综合九色综合欧美狠狠| 久久久国产精品一区二区三区| 午夜亚洲伦理| 欧美一级播放| 久久激情综合| 久久先锋影音| 免费精品视频| 欧美成人免费在线视频| 女人色偷偷aa久久天堂| 美女视频黄 久久| 欧美成人一二三| 欧美激情一区二区三区蜜桃视频 | 欧美一级大片在线观看| 亚洲欧美综合精品久久成人| 亚洲一区视频在线| 欧美一级淫片播放口| 久久精品中文| 欧美大片在线看免费观看| 欧美日本乱大交xxxxx| 国产精品超碰97尤物18| 国产伦精品一区二区三区在线观看 | 国内精品视频在线观看| 黄色另类av| 亚洲精品日韩激情在线电影| 9i看片成人免费高清| 亚洲一本大道在线| 久久成人国产精品| 欧美高清hd18日本| 一二三区精品| 久久精品91久久久久久再现| 免费一级欧美在线大片| 欧美日韩中文字幕| 国产综合视频| 99视频一区| 久久精品国产2020观看福利| 欧美mv日韩mv亚洲| 亚洲欧洲日产国码二区| 亚洲一区图片| 欧美ed2k| 国产欧美日韩三区| 亚洲精品1234| 欧美一区二区视频免费观看| 欧美a级理论片| 亚洲特黄一级片| 老**午夜毛片一区二区三区| 欧美日韩在线一区二区| 黄色精品网站| 亚洲女性裸体视频| 免费成人av资源网| 中文亚洲欧美| 嫩草影视亚洲| 午夜精品偷拍| 亚洲欧美经典视频| 裸体丰满少妇做受久久99精品| 欧美日韩精品综合| 一区二区三区在线视频观看| 亚洲午夜精品久久久久久app| 久久久综合精品| aa亚洲婷婷| 欧美mv日韩mv国产网站| 国产无一区二区| 一区二区三区精品在线| 女生裸体视频一区二区三区| 亚洲一级片在线观看| 欧美国产精品va在线观看| 国产又爽又黄的激情精品视频 | 国产一区二区三区奇米久涩| 亚洲免费观看在线视频| 久久久久久成人| 亚洲视频免费观看| 欧美多人爱爱视频网站| 在线电影一区| 久久久久久97三级| 亚洲欧美久久| 国产精品亚洲欧美| 亚洲在线电影| 日韩午夜免费| 欧美精品激情在线| 亚洲区欧美区| 欧美国产一区在线| 久久精品官网| 国产综合久久久久久鬼色| 午夜在线精品| 亚洲一区二区三区乱码aⅴ蜜桃女| 欧美日本精品在线| 亚洲精品一线二线三线无人区| 久久偷窥视频| 久久精品国产在热久久| 国产一区二区三区四区三区四| 西西人体一区二区| 亚洲尤物精选| 国产精品稀缺呦系列在线| 亚洲制服av| 亚洲一区二区三| 国产精品美女久久福利网站| 一区二区三区四区五区视频| 亚洲精品国产精品乱码不99按摩| 欧美国产免费| 一本色道久久综合亚洲精品不卡| 91久久线看在观草草青青| 欧美交受高潮1|