亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

在C++中偵測內嵌型別的存在

2019-11-17 05:06:05
字體:
來源:轉載
供稿:網友
  動機(Motivation)  假設一所大學的注冊系統提供了一個注冊函數:
  template<class T>  void Register(T person)  {  Register(person,typename T::person_tag());  };  而對于注冊者有以下幾種標識:
  strUCt student_tag{};  struct teacher_tag{};  還有Register的幾個供內部使用的重載版本:
  template<class T> void Register(T p,student_tag){...} //注冊學生  template<class T> void Register(T p,teacher_tag){...} //注冊教師  并規定學生類一定要在內部typedef student_tag person_tag ;教師類typedef teacher_tag person_tag ;這樣,當傳給起初的那個Register的對象為學生類對象時,typename T::person_tag()其實構造了一個student_tag對象,從而激發函數重載,調用Register內部版本的template<class T> void Register(T p,student_tag);版本。其他情況亦均有對應。這是泛型編程里的常用手法,STL里屢見不鮮?! 栴}是,現在學校里假如不止學生教師,還有工人,警衛等其它人員。假如他們不會在類內部typedef任何東西,則Register需要一種機制以確定T內部是否typedef了某個標識符(例如person_tag)。假如沒有,就默認處理。假如有,則再進行更具體的分類。  實現(Implementation)  這個問題可能有兩個實現途徑。  第一,利用函數重載,具體如下:
  typedef char (&yes_type)[1]; //sizeof(yes_type)==1  typedef char (&no_type)[2];  //sizeof(no_type)==2  以上的兩個typedef用于識別不同的重載函數。char (&)[1]表示對char[1]數組的引用,所以sizeof(char(&)[1])==sizeof(char[1])==1;注重圍繞&符號的一對圓括號,它們是必要的,假如沒有將會導致編譯錯誤,正如char* [1]將被解析為char*的數組,char& [1]將被解析為引用的數組,而后者是非法的。將&用圓括號包圍則改變了運算符的結合優先序,這將被解析為對char[1]數組的引用。



更多文章 更多內容請看C/C++技術專題專題,或

  template<class T>  struct does_sometypedef_exists  {  template<class U>  static yes_type check(U,typename U::key_type*  =0); //請注重* 和=之間的空格,那是必要的,否則  //編譯器會將它解析為Operator*=操作符  static no_type check(...);  static T t;   //聲明  static const bool value=sizeof(check(t))==sizeof(yes_type);  };  在我的VC7.0環境下,這樣測試是成功的:
  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;  };  下面我為你講解它的原理?! ‘斶M行重載解析時,編譯器會首先嘗試具現化可以匹配的模板函數并將它們納入到有待進行重載解析的函數的候選單之列,在本例中,當typename T::key_type不存在時,check的第一個模板版本不能具現化(因為其第二個參數型別typename U::key_type*不存在),所以只能匹配第二個版本。當typename T::key_type存在時,第一個模板函數可以具現化,且可以匹配(注重第二個參數為缺省參數),所以無疑編譯器會匹配第一個版本,因為C++標準保證:只有當其它所有重載版本都不能匹配的時候含有任意型別參數列表的版本(在本例中那是no_type check(...))才會被匹配。



更多文章 更多內容請看C/C++技術專題專題,或   一個值得注重的地方是:check的第一個版本只能是模板函數,因為當編譯器推導型別的過程中發現該模板函數不能具現化時它就不去具現化它,而不是產生編譯錯誤(除非沒有其它可匹配的重載版本)。
因為編譯錯誤只有將代碼編譯的過程中才會產生,而既然模板沒有具現化,那么該模板實際上并沒有經過編譯?! ∪欢偃缢皇悄0搴瘮?,則隨著does_sometypedef_exists類的具現化。它也會被具現化,然而假如不存在T::key_type,那么,該函數就成為非法?! ∵€有一個值得注重的地方是:does_sometypedef_exists內部的static T t;只是一個聲明,并不占用內存空間,更妙的是,因為是個聲明,所以編譯器根本不會對它初始化,所以它的默認構造函數就根本不會被執行,事實上,編譯器在這種情況下甚至不會去看一看它是否有可用的默認構造函數,它只需要型別信息就足夠了,不是么?因此,即使由于某些原因T的默認構造函數被禁止(設為PRivate)(例如,想讓T從堆上創建),那么以上的traits也不會通不過編譯?!钡牵鹊?!”你仿佛意識到了問題:“check的參數是傳值的!這時假如T的拷貝構造函數是私有的將會發生什么事情呢?”事實是,根本不用去擔心,在sizeof的世界里,根本不會發生求值行為,編譯器只需要有關型別的信息。在編譯器內部蘊涵有一個巨大的型別推導系統。無論sizeof(...)里的表達式多么復雜,其型別都會最終在編譯期被正確推導出來。而對于sizeof(check(t)),編譯器有了函數的返回值型別信息就夠了,它并不會去執行函數的代碼,也不會做實際的傳參行為,所以拷貝構造也就無從發生?! 〉@里有一個十分怪異的問題(在我的VC7.0環境下存在),假設我們增加一個新類:
  struct C  {  template<class T>  struct key_type{};  //請注重這是個模板類  };  按理說,這種情況下does_sometypedef_exists<C>::value應該為false,因為第一個重載版本的typename U::key_type*不能被推導為C::key_type* (C::key_type是個模板,它需要模板參數來具現化),然而在我的VC7.0下它通過編譯了,并且結果為true!!(就是說重載解析為第一個check函數)假如我將check的第一個版本作一點小小的改動,像這樣:
  template<class U>  static yes_type check(U,typename U::key_type* = (typename U::key_type*)0);  我僅僅加了一個轉換,編譯器就開始抱怨說使用模板類(它指的是C::key_type)需要模板參數了。我作了另外的種種測試(甚至我發現假如將10傳給它的第二個參數,編譯器會說不能將int轉換為C::key_typ*,是的,這是編譯錯誤的原文,這是否表示編譯器承認C::key_type*為一種型別呢?我不知道)。結論是只有當typename U::key_type*作為模板函數的參數型別時這種情況才會發生。



更多文章 更多內容請看C/C++技術專題專題,或
  第二個實現是利用模板偏特化及默認模板參數的規則:
  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>  {                            //上面的兩個typename都是必要的?! tatic const bool value=true;  };  這看起來很小巧,僅僅使用了模板偏特化。但是請耐心聽我解釋?! 〖偃鐃ypename X::key_type存在(假設X為任意類),則does_sometypedef_exists_1<X>首先由模板推導將does_sometypedef_exists_1的模板參數T匹配為X,則其偏特化版本因而被推導為:

  struct does_sometypedef_exists_1<X,typename check_helper<X,typename X::key_type>::type>  而typename check_helper<X,typename X::key_type>::type根據check_helper的定義其實就是X,所以該偏特化版本其實被推導為:
  struct does_sometypedef_exists_1<X,X>  所以,假如你這樣測試:does_sometypedef_exists_1<X>::value,根據does_sometypedef_exists_1缺省定義(第二個模板參數默認為T),你寫的相當于:does_sometypedef_exists_1<X,X>::value?! 《鶕厦娴耐茖В偃鐃ypename X::key_type存在,則does_sometypedef_exists_1的偏特化版本也存在且形式為:
  更多文章 更多內容請看C/C++技術專題專題,或

  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。這正是我們所想要的結果?! y試(Test)  現在對我們的兩個實現版本測試一下吧,假設有一下幾個類:
  struct A{};  //沒有key_type  struct B{typedef int key_type;};  //typedef  struct C{void key_type(void){}};  //key_type為成員函數  struct D{static const bool key_type=false;}; //key_type為靜態常量數據成員  const bool D::key_type;  //定義,D里面的是聲明  struct E{  template<class>  struct key_type{};  //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<<' ' 



更多文章 更多內容請看C/C++技術專題專題,或

  <<does_typedef_exists<D>::value<<' '  <<does_typedef_exists<E>::value<<' '  <<std::endl;  return 0;  };  在我的VC7.0編譯平臺上:  假如使用第一種實現,這將輸出:0 1 0 0 1  假如使用第二種實現,這將輸出:0 1 0 0 0  很顯然,兩種實現對于struct E給出的結果不一樣。事實上,我們希望該traits對E這種情況給出的結果為1。從這一點講第一種實現在我的編譯器上已經神差鬼使的成功了,而第二種實現還沒有。不管怎樣,我們都必須試圖找到一種方法來實現它。這種方法不可以像實現一那樣依靠與編譯器的可能的“一時糊涂”,它應該是以C++標準的規則為依據的。Paul Mensonides提供了一種方法,然而在我的VC7.0上編譯不能通過。后面我會介紹它?! 「倪M(Improvement)  第一種實現還可以做一點改進,像這樣:
  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的第一個參數,會使代碼看上去更簡潔和更可靠一些。  封裝(Encapsulation)




更多文章 更多內容請看C/C++技術專題專題,或
  現在我們的traits只能偵測typename T::key_type的存在性,我們需要一個擴充的機制,以讓我們能夠偵測任意名稱的內嵌型別的存在性。我們使用宏:
  #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>  經過這重封裝,當你要偵測某個名稱的內嵌型別如some_type時,你先在任何函數之外寫這樣的代碼:
  IMPLEMENT_TYPEDEF_EXISTS(some_type)  這將會擴展成一個名為does_sometypedef_exists_some_type的模板類,然后你這樣使用它:
  DOES_TYPEDEF_EXISTS(X,some_type)::value;  這將偵測類X中有沒有some_type。不將::value直接納入到宏中的原因是為了保留traits編程的風格?! aul Mensonides對內嵌template的偵測方法  Paul Mensonides是Boost庫的preprocesser部分的設計者,那完全是一個宏的世界,也是Boost庫中的一個十分精巧的部分。我最初是在comp.lang.c++.moderated上看到它關于這個問題的解答的。


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



更多文章 更多內容請看C/C++技術專題專題,或

  //以下是偏特化  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. :(,后面有支持更多模板參數的版本,從略  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< 


更多文章 更多內容請看C/C++技術專題專題,或

  typename U::template key_type<null_t, null_t>  //匹配兩個模板參數的內嵌模板  >::type*  );
  
  // etc. :( 后面有支持更多模板參數的版本,從略  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;           //則將value設為false  };  Paul Mensonides說它能夠工作,我也覺得根據標準它也該能夠工作,但事實是在我的VC7.0上編譯器有一大堆抱怨。我試了其它各種方法,結果總是類似的編譯錯誤將我擋住。我希望它在你的編譯器上能夠工作。  這里的原理是這樣的,假如類型X有內嵌模板型別定義key_type,則has_template_key_type中的返回yes_type的那些成員函數總有一個能夠與它匹配,而其它則不會被具現化(VC7.0仿佛總試圖將其它的也具現化了,結果它總會抱怨說模板參數太少或太多)?! ∪欢鳳aul Mensonides的這個解決方案還有個問題:假如那個內嵌的模板類的定義像如下這個樣子:

  template<int>  struct key_type{};  則將沒有任何一個返回yes_type的重載版本能和它匹配,看看split類的定義吧,它的template template模板參數的形式是template<class[ ,class ,...]> class T,而上面的key_type的形式為template<int> class key_type,它們無法匹配,假如試圖再加入一個能與其匹配的split偏特化版本:
  template<template<int>class T,int T1> struct split<T<T1>  >{...};  這也是不實際的。因為int和class可能有無窮多種組合。假如key_type再變成template<int,class> class key_type呢?假如...,總之,如你所見,以int作為模板參數的加入使事情有了無限多種可能。split將窮于應付?! 〗Y論(Conclusion)  對于最后我提出的問題,仿佛沒有一個好的解決方案。所以只能放棄這種內嵌template的可能,假定情況是單純的。對于后者,這種技術有教好的表現。





更多文章 更多內容請看C/C++技術專題專題,或

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
91日本在线观看| 69精品小视频| 国产精品亚洲片夜色在线| 91伊人影院在线播放| 久久777国产线看观看精品| 亚洲成人999| 91国内揄拍国内精品对白| 成人av在线亚洲| 久久国产精品久久精品| 国产精品欧美日韩一区二区| 国产视频在线观看一区二区| 狠狠色狠色综合曰曰| 成人激情春色网| 色婷婷成人综合| 欧美极品少妇xxxxⅹ喷水| 国产精品69久久久久| 亚洲精品国产美女| 伦伦影院午夜日韩欧美限制| 亚洲天堂久久av| 国产精品国产福利国产秒拍| 波霸ol色综合久久| 97久久精品在线| 日韩国产激情在线| 久久精品一区中文字幕| 日韩欧美中文字幕在线播放| 亚洲人成网站在线播| 亚洲图片在线综合| 日韩欧美中文第一页| 久久久久国色av免费观看性色| 亚洲综合在线播放| 日韩第一页在线| 亚洲国产成人在线视频| 亚洲欧美在线免费| 国产成人拍精品视频午夜网站| 欧美放荡办公室videos4k| 欧美激情精品久久久久久大尺度| 性亚洲最疯狂xxxx高清| 欧洲永久精品大片ww免费漫画| 欧美日韩第一视频| 狠狠操狠狠色综合网| 国产精品黄色影片导航在线观看| 国产日产亚洲精品| 精品无人区太爽高潮在线播放| 成人免费淫片aa视频免费| 国产精品电影在线观看| 91大神福利视频在线| 亚洲性生活视频| 日本免费在线精品| 国产一区二区欧美日韩| 日韩免费av一区二区| 欧美最近摘花xxxx摘花| 亚洲高清福利视频| 国产欧美日韩高清| 午夜精品99久久免费| 国产999精品久久久影片官网| 欧美精品精品精品精品免费| www国产精品视频| xx视频.9999.com| 久久久久北条麻妃免费看| 欧美电影第一页| 成人性生交大片免费看小说| 69视频在线播放| 欧美一区二区三区图| 日韩成人在线播放| 久久精品视频在线| 曰本色欧美视频在线| 26uuu日韩精品一区二区| 亚洲а∨天堂久久精品喷水| 国产精品精品国产| 亚洲三级黄色在线观看| 久久亚洲私人国产精品va| 久久久久久网站| 亚洲人成欧美中文字幕| 91久久夜色精品国产网站| 午夜免费久久久久| 中文字幕在线看视频国产欧美在线看完整| 欧洲成人午夜免费大片| 91精品国产91久久久久久最新| 亚洲电影免费观看高清完整版在线| 久久久精品影院| 久久精品中文字幕一区| 欧美自拍视频在线观看| 久久久久99精品久久久久| 国产成人久久精品| 欧美视频在线观看 亚洲欧| 91亚洲va在线va天堂va国| 91色在线观看| 6080yy精品一区二区三区| 久久中文字幕在线| 美日韩精品免费观看视频| 欧美激情免费观看| 国产福利精品在线| 日韩中文字幕在线看| 91免费高清视频| 另类视频在线观看| 亚洲天堂2020| 热re99久久精品国产66热| 欧美大学生性色视频| 国产精品亚洲一区二区三区| 日韩av免费在线播放| 在线播放国产一区中文字幕剧情欧美| 亚洲天堂av在线免费观看| 久久久国产一区| 日本精品一区二区三区在线播放视频| 国产成人一区二区在线| 日韩精品视频免费专区在线播放| 欧美在线性爱视频| 欧美麻豆久久久久久中文| 日韩中文在线中文网三级| 国产91网红主播在线观看| 欧美成人剧情片在线观看| 欧美电影在线观看网站| 26uuu另类亚洲欧美日本一| 综合欧美国产视频二区| 国产精品www色诱视频| 日韩av在线天堂网| 夜夜嗨av色一区二区不卡| 日韩免费观看网站| 亚洲国产精品成人av| 亚洲欧美变态国产另类| 日韩免费观看高清| 欧美午夜精品久久久久久浪潮| 55夜色66夜色国产精品视频| 久久九九免费视频| 一区国产精品视频| 欧美综合第一页| 亚洲精品影视在线观看| 亚洲国产精品va在线观看黑人| 欧美日韩精品在线观看| 久久久成人av| 日本精品久久久| 久久影院中文字幕| 精品网站999www| 亚洲天堂av高清| 国产精品成人av性教育| 久久久久久久久久久成人| 综合激情国产一区| 国产成人精品a视频一区www| 51ⅴ精品国产91久久久久久| 欧美一区二区视频97| 亚洲激情在线观看| 久久久久久久一区二区三区| 亚洲影院色在线观看免费| 日韩成人中文字幕| 亚洲天堂av电影| 国产午夜精品理论片a级探花| 国产精品夜色7777狼人| 亚洲毛茸茸少妇高潮呻吟| 亚洲一区二区三区xxx视频| 国产做受69高潮| 亚洲精品之草原avav久久| 上原亚衣av一区二区三区| 国产在线a不卡| 亚洲大尺度美女在线| 亚洲国产欧美久久| 欧美日韩一区二区精品| 国内外成人免费激情在线视频| 久久伊人免费视频| 久久影视电视剧免费网站| 久久中文字幕在线视频| 中文字幕亚洲一区| 国产在线高清精品| 日韩人在线观看| 欧美日韩在线观看视频|