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

Impossible is nothing  
  愛過知情重醉過知酒濃   花開花謝終是空   緣份不停留像春風來又走   女人如花花似夢
公告
日歷
<2025年11月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456
統計
  • 隨筆 - 8
  • 文章 - 91
  • 評論 - 16
  • 引用 - 0

導航

常用鏈接

留言簿(4)

隨筆分類(4)

隨筆檔案(8)

文章分類(77)

文章檔案(91)

相冊

搜索

  •  

最新評論

閱讀排行榜

評論排行榜

 

C++ 的另一個新世界

C++ 的 MetaProgramming

廢話就不說了, 按照C的傳統慣例,介紹programming的最好方式就是show代碼, 第一個例子就是Hello,world, 這篇文章也不例外

在任何一個cpp文件中,輸入

struct hello_world; //forward declaration
struct A : hello_world
{
};

然后編譯..,注意我沒有說"編譯運行",而僅僅說的是"編譯", 如果不意外的話,在你編譯器的輸出窗口會出現
base class hello_world undefined
或者
base class `hello_world' has incomplete type

等類似的語句, 至少在你的屏幕上打印出了 "hello_world" 字樣,對吧? 對了,這就是這個例子的目的, 我不也打印出來的嗎?

上面這個簡陋的例子闡述了一個重要的現象, "編譯時"而不是運行時, 這就是meta programming的世界, 一個處于編譯期間, 而不是運行期間的世界.

運行時我們能作的事情很多在編譯時我們不能作, 例如我們不能調用函數, 我們不能創建對象, 我們也不能設置斷點, 一切都交給你的C++編譯器.


接下來, 首先回顧一下一些C++的基本模板知識.
由于C++的編譯器符合ISO 標準的程度不一, 我使用的是VC++ 6 和 gcc version 3.2.3 (mingw special 20030504-1)
下面的例子我在這兩個編譯器中都測試過.

C++模板的最經常的認識就是STL中的容器, 例如
vector<int> 就是一個可以裝int的動態數組, vector<sharp*> 就是一個可以裝sharp對象指針的數組.

然后我們還需要一點點模板特化的知識.
例如

template<class T> struct Foo {};

這是一個通用模板, 必配任何類型, 如果我們需要對int進行特別處理, 那么

template<> struct Foo<int>  {}; 

這樣就實現了對 int 類型的完全特化 . VC6 只支持完全特化, 不支持偏特化, 或者部分特化.
不過還是稍微介紹一下:
什么是遍特化了? 還就上面這個例子而言, 如果我們想對所有的指針進行特化, 那么應該是
template<class T> struct Foo<T*> {}

那么什么又是部分特化了, 看看這個:
template<class T, class U> struct bar {};

template<class T> struct bar<T, int>  {};

后面這個就是對U模板參數的部分特化,使得 U為 int的時候 使用這個版本.

這篇文章由于針對VC6, 因此你不會遇到偏特化和部分特化的情況.


hello,world的例子以后, 我們準備干點有意思的事情. 例如求兩數之和. 首先看看在運行時的函數我們應該如何實現:

定義:
int sum(int x, int y) { return x+y; }

調用:
int j =  sum(3, 4)


那么如何用meta programming的方式實現了?

meta programming 由于處于編譯器, 因此給它的參數必須是編譯時就可以確定的, 在當前C++中允許的為,integer 和 type.

就上面這個例子, 對于模板的調用應該是:
int j = sum_<3,4>::value;

3,4 都為整形常量, 編譯時確定, 然后返回值如何取得? 記住在編譯時是無法進行真正的函數調用的,因此我們只有通過定義在模板類中的一個常量
來獲得返回結果. 最簡單的方法就是在一個 struct定義一個匿名的enum .

sum的定義如下:
template<int x, int y>
struct sum_
{
  enum { value = x + y };
};

然后你可以編譯后"運行"檢查檢查看看運行結果

#include <iostream>

using namespace std;

template<int x, int y>
struct sum_

{
   
    enum { value = x + y };

};

int main()

{
   
   cout << sum_<3, 4>::value << endl;
   
   return 0;

}


上面之所以使用struct sum_而不是class sum_是因為struct的默認作用范圍上public, 可以省略幾個鍵擊.

這個例子通用展示了一個重要的觀點, 對應通常可以在運行時調用的函數
int foo(int a, int b)
其對應的meta 實現為:
foo<a, b>::value
也就是我們的foo現在為一個struct name, 參數通過< > 中的模板投遞, 結果通過 ::value 獲得其中定義的一個匿名enum值.

那么如何計算 3,4,5的和?
你可能會想如下:
 sum_<3,4,5>::value

但是c++不支持一個模板類使用不同的參數參數存在, 換一個方式,
你如何在 int sum(int a, int b) 存在的情況下獲得3個數的和?
我們可以這樣:
sum( 3, sum(4, 5))
有lisp 背景的可能發現這很符合他們的思考方式, 至少以我浮淺的emacs lisp知識, 不使用中間變量. 同樣,meta中你也可以這樣使用:

sum_<   //開始參數
sum_< 3 //第一個參數為3
sum_< 3, sum_<   //第二個參數是另外一個表達式的結果
sum_< 3, sum_<4, //這個表達式的第一個參數為4
sum_< 3, sum_<4, 5> //這個表達式的第二個參數為5
sum_< 3, sum_<4, 5>::value //通過::value獲得這個表達式的結果
sum_< 3, sum_<4, 5>::value >::value //然后獲得整個表達式的結果

ok, 就這么多, 看出來沒有, 再解釋一次, 將上面我們的
  cout << sum_<3, 4>::value << endl;
   

中4的位置用一個sum_ 替換就得到了我們需要的三個數之和.


  cout << sum_<3, ?? >::value << endl;
   
 ===>
   ?? =  sum_<4, 5>::value

 ===> then
  cout << sum_<3, sum_<4, 5>::value >::value << endl;
   


如果我需要算 2, 3, 4, 5之和呢?
同樣簡單, 你將上面的3, 4 ,5 中的任何一個常量替換成對sum_ 進行一個調用就可以了.
例如:

out << sum_< ?? , sum_<4, 5>::value >::value << endl;
   

?? =  sum_< 2, 3 >::value

合并后為

cout << sum_< sum_<2, 3>::value , sum_<4, 5>::value >::value << endl;
運行的結果為 14.


這就是meta中最簡單的一個例子, 順序調用, 如果你看明白了同時覺得有點意思的話, 下來我們講講
循環語句, 通常我們寫程序都避免遞歸, 因為容易造成堆棧和大腦溢出,但是在 meta 中必須使用遞歸的方式.

下面看看一個計算"階乘"的例子, 其實這個才真正是meta 中的hello,world.

先看后面, 我們調用的方式:

cout << fac<5>::value ;  // 結果應該是 5 * 4 * 3 * 2 * 1 = 120;

通過前面的知識, 你知道fac是一個template struct的名字, 有一個模板參數int, value是其中的一個匿名枚舉變量
于是你可以毫不猶豫的寫下:

template<int i>
struct fac
{
  enum { value = ??? };
};

但是在value的地方你卡住了, 如果根據 i 得到 value?

讓我們將大腦從"編譯時世界"切換到"運行時的世界", 你如何寫一個通常的遞歸函數來計算階乘?

int fac(int n)
{
  if( n == 1)
    return 1;
  return n * fac(n-1);
}

注意 n == 1是一個遞歸退出條件,先不考慮n為1時的遞歸退出,
其他情況下是將n 乘以 fac (n - 1). 有了前面的sum_ 知識, 你應該可以推出 value = ???
中的??? 應該是
n *       //n*
fac       //調用下一個fac
fac<n-1>  //參數為n-1
fac<n-1>::value //獲得結果

因此fac "函數"的模板實現就是

template<int i>
struct fac
{
  enum { value = i * fac<i - 1>::value };
};

然后我們再考慮遞歸退出條件, 為1時value應該為1, 拿起C++中的特化模板武器來
template<>
struct fac<1>      //參數為1時的特化
{
  enum { value = 1 };
};

這樣就將參數為 1 時的返回值設置為1, 結束遞歸.

這樣,當你輸入
cout << fac<5>::value << endl;
時,編譯器會實例化
template<>
struct fac<5>
{
  enum { value = 5 * fac<4>::value };
};

由于fac<5> 需要 fac<4> , 因此然后實例化 fac<4>, 同樣的原因, fac<3> , fac<2>, fac<1>

到fac<1> , 編譯器發現了一個特化版本fac<1> 匹配, 在fac<1>中的value已經是一個常量, 不依賴其他的fac實例, 遞歸退出, 最后
我們得到最終結果120 .


easy, 對不?

然后再介紹 if語句.
還是上面的這個fac例子, 負數的階乘是沒有意義的,先不考慮數學問題,假設我們希望在這個情況下返回-1,如何實現?

如果我們輸入fac<-2>::value , 那么編譯器會嘗試實例化fac<-3>, fac<-4>, ......
你發現這是一個無限遞歸條件, 在運行時會造成你堆棧溢出, 由于我們在"編譯時世界", 取決于你編譯器, 最終總有結束的時候.

例如VC6 報錯: fatal error C1202: recursive type or function dependency context too complex
G++報錯     : template instantiation depth exceeds maximum of 500

因此我們需要一個if判斷語句, 發現當模板參數 < 1的時候返回 -1.

按照測試先行的原則, 我們可以預計
fac<-1>::value == -1  是成立的.
現在的問題是如何實現? 下次再說吧! 也給個機會折磨折磨你的大腦. 記住, 模板不僅僅可以通過enum返回整數, 還可以通過嵌套 typedef返回一個類型.

上回說到一個fac的版本, 希望在負數的情況下返回-1, 而不是無限遞歸下去.
還是按照我們的思維, 先寫個對應"運行時世界"的版本.

int safe_fac(int n)
{
  if( n < 1)
     return -1;
  return fac(n);
}

這個if邏輯很簡單, 如果模板參數<1, 那么直接返回 -1, 否則 還是使用前面的fac那個版本.
好, 轉換成我們的meta 版本.

你想,用個 ?: 運算符不就解決了嗎?

template<int n>
struct safe_fac
{
  enum { value =  (n < 1 ? -1 : fac<n>::value ) };
};

可惜不對,  ?= 只有在"運行時世界"才能使用. 那么 value 后面的???寫什么好呢?

先輕松輕松, 寫一個if的meta 版本, 我敢保證你能看得懂.
template< bool b , class T, class U>
struct if_
{
  typedef T type;
};

注意了, 如果以前我們提到的例如sum_, fac等meta functions(其實就是c++中的模板類, 稱之為meta function是因為它們就像是function)
是通過一個 在enum中的value 返回整形的話, 上面剛剛的if_這個例子就展示了 meta中的另外一個武器, 通過typedef 一個type 返回一個類型.

如果我們這樣調用
if_<true, int, double>::type  的結果就是 int 類型, 注意是"類型", 不是對象.

我們想在b為false的時候返回第二個類型U, 即:
if_<false, int, double>::type 的返回結果是double

那么還是很簡單, 部分特化 b 參數就可以了.即:
template<class T, class U>
struct if_<false, T, U>
{
  typedef U type;
};

我最前面說了, VC6不支持部分特化, 但是別忘了計算機時間的一條公理:
任何計算機問題都可以通過增加一層來解決. 大部分VC6中的模板的問題還是可以解決的. 如果你不使用VC6, 這部分就不用看了.

VC6是支持全部特化的, 因此我們可以將true, false特化出來

template<bool>
struct if_help
{
   ...
};

template<>
struct if_help<false>
{
   ...
};

這個在vc6中是支持的. 然后我們還需要兩個額外的類型參數T,U, 這可以通過嵌套類來實現. 即

template<bool>
struct if_help
{
  template<class T, class U>
  struct In
  {
     typedef T type;
  };
};

template<>
struct if_help<false>
{
  template<class T, class U>
  struct In
  {
     typedef U type;
  };
};

然后我們真正的if_ "meta 函數"如下定義:

template<bool b, class T, class U>
struct if_
{
   typedef if_help<b>::In<T, U>::type type;
};

先根據b的內容實例化一個對應的if_help, : if_help<b>
然后給其中的In模板投遞T,U參數, ::In<T, U>
然后通過type獲得其中的返回類型 ::type
最后typedef type一下作為自己的返回類型, 這樣外部就可以通過
if_<true, int, double>::type  獲得返回的類型了.

上面if_ 的實現實際上要用幾個C++關鍵字修飾一下:
typedef if_help<b>::In<T, U>::type type;
 ===>
typedef typename if_help<b>::template In<T, U>::type type;

為什么要加上typename 和 template, 這個解釋起來到是很費勁. 有空再說.

好了, 從模板的語法世界中清醒過來, 現在你知道的是, 我們有了一個if_ 的meta函數, 接受3個參數bool b, class T, class U,
如果b為true, 那么它的返回值 (通過 if_::type 返回) 就是T,
如果b為false, 那么它的返回值 (通過 if_::type 返回) 就是U.

前面我提過了, 參數是通過<>來傳遞的, 因此一個例子就是

if_                          //函數名
if_<true,                    //第一個參數, bool 型
if_<true, int                //第二個參數, 類型
if_<true, int, double        //第3個參數,  類型
if_<true, int, doubble>      //右括號表示參數結束
if_<true, int, double>::type //通過::type獲得返回結果, 不是value了, 當然這僅僅是一個命名慣例.

因此上面的那個 if_<true, int, double>::type 返回的就是 int,
在"運行時世界", 你可以如下使用:

for( if_<true, int, double>::type i = 0; i < 10; i++) {
  cout << "line " << i << "\n";
}

等同于
for( int i = 0; i < 10; i++) {
  cout << "line " << i << "\n";
}

當然對于這個例子這樣使用是"有病". 我們等會會用到if_來實現前面的  safe_fac 實現了.

注意我說的返回值并不是前面sum_例子中的整形了, 這個時候返回一個類型. 類型不是實例對象, 這點我想你應該清楚.
編譯時不可能返回對象, 因為要調用構造函數, 要確定對象地址, 我們還沒有進入到" 運行時世界" , 對吧?
實際上meta programming 最重要的使用并不是前面我們提到過的sum_, fac這些, 因為畢竟拿個計算器算一下也花不了幾個時間.
但是返回type就不同了.
那么type可以是什么呢? 可以是int, double這樣的基本類型, 也可以我們前面的 sum_ 模板類等等.

然后再看safe_fac的實現:
先不考慮 <1 的情況, 那么value就應該是直接調用以前的 fac 函數

template<int n>
struct safe_fac
{
  enum { value = fac<n>::value };
};

然后再使得 >= 1時才使用fac函數, 那么利用我們前面的if_, 先忽略語法錯誤, 那么可以如下

enum
{
value = if_< n < 1,???,  fac<n> >::type::value
};

首先, n < 1 為真時返回一個類型???, 暫時我們還沒有實現, 為false時返回
fac<n>類型, 然后通過::type獲得返回的類型,或者為???, 或者為fac<n>,
然后通過::value得到這個類型的整數結果.

那么 ??? 應該是什么呢? 當然不能直接是-1, 否則 -1::value 就是語法錯誤.

因此我們可以定義一個簡單的"函數", 返回-1:

struct return_neg1
{
   enum { value = -1 };
};

如果我們需要返回-2怎么辦? 又定義一個return_neg2 "函數"? 干脆我們一勞永逸, 定義如下:

template<int n>
struct int_
{
   enum { value = n };
};

這樣int_ 這個"函數"就是你給我什么, 我就返回什么. 不過int_是一個類型, 例如:
通過如下調用 int_<3>::value  返回它的結果3.

有了這個, 我們的代碼就如下:

value = if_< n < 1, int_<-1>,  fac<n> >::type::value

原理清楚了, 最終的版本就是:

template<int n>
struct safe_fac
{
  enum { value = if_< n < 1, int_<-1>,  fac<n> >::type::value };
};

試試:
cout << safe_fac<-1>::value 輸出 -1.

循環(遞歸表示), 條件判斷, 順序執行都有了, 剩下的就看你自己了.

--------------------------------------------------------------------------------
Boost中的mpl (meta programming library) 提供了一個專門用于metaprogramming的library, 同時前面提到的if_, int_等等就
是從mpl中拷貝來的, 當然我簡化了很多.

Modern C++ Design 其中的Typelist將meta progamming的循環(就是遞歸)發揮得淋漓盡致, 在侯捷的網站上www.jjhou.com有免費的前4章可讀. Typelist在第三章.
書中序言部分, Effective C++的作者Meyers說到, 如果第3章的typelists沒有讓你感到振奮, 那你一定是很沉悶.
就我親身體驗, 我覺得Meyers可能是說到委婉了些:
如果第3章的typelists沒有讓你感到振奮, 那1%的可能是你是很沉悶, 99%的可能是你沒有看懂. I did.
GoF之一的 John Vlissides同樣提到了typelists這一章就值得本書的價格. I believe.

另外, 我發現即使在這本讓人抓狂的天才之作中, 作者仍然使用了n個TYPELIST_n 這樣的預處理宏來處理多個type的情況, 但是在sourceforge
下的Loki庫中我發現已經有了一個MakeTypelist"函數", 看來 meta programming 確實是, 啊...
是不是作者當時都沒有預見到還能夠以C++編譯器內建的模板能力, 而不是依賴預處理宏來處理typelist.?

又, mpl中有專門裝type的容器....

posted on 2006-02-27 23:37 笑笑生 閱讀(507) 評論(0)  編輯 收藏 引用 所屬分類: C++語言
 
Copyright © 笑笑生 Powered by: 博客園 模板提供:滬江博客
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲黄色性网站| 国产欧美在线视频| 国产精品theporn88| 91久久综合亚洲鲁鲁五月天| 亚洲国产成人一区| 国产精品久久久久久久7电影 | 欧美在线高清| 91久久国产自产拍夜夜嗨| 亚洲欧美精品在线观看| 亚洲激情成人网| 国产一区二区三区av电影| 欧美电影在线观看| 在线亚洲激情| 亚洲美女免费视频| 欧美激情在线播放| 99国产精品国产精品久久| 久久偷窥视频| 一级成人国产| 欧美午夜视频在线| 最新日韩在线视频| 欧美亚洲视频| 亚洲第一中文字幕在线观看| 亚洲一区二区伦理| 午夜欧美不卡精品aaaaa| 国产亚洲精品aa午夜观看| 91久久香蕉国产日韩欧美9色| 最新日韩在线| 在线视频成人| 中文一区二区在线观看| 美女性感视频久久久| 中文精品视频| 一区二区av| 亚洲视频在线播放| 亚洲一区中文| 久久国产日韩| 欧美色另类天堂2015| 久久国产精品一区二区| 激情综合视频| 亚洲福利一区| 99国产精品久久久久久久久久| 欧美二区在线播放| 国产欧美日韩精品在线| 亚洲国产精品视频一区| 一本色道久久综合狠狠躁篇的优点| 亚洲一区二区在线看| 麻豆乱码国产一区二区三区| 欧美1区3d| 午夜精品电影| 性久久久久久久久久久久| 欧美一区二区三区在| 亚洲欧美日韩直播| 久久精品av麻豆的观看方式 | 免费日韩视频| 欧美色图天堂网| 最新日韩中文字幕| 久久久久国产精品厨房| 性18欧美另类| 国产日韩欧美视频在线| 亚洲免费视频网站| 99香蕉国产精品偷在线观看| 亚洲综合国产| 国产日韩在线不卡| 久久亚洲高清| 久久久久久电影| 亚洲国产高清aⅴ视频| 老司机免费视频一区二区三区| 这里只有精品在线播放| 亚洲卡通欧美制服中文| 国产伦一区二区三区色一情| 欧美在线观看网站| 久久精品综合| 亚洲黄色在线观看| 日韩亚洲欧美成人| 国产一区二区电影在线观看| 一区二区精品| 久久久99国产精品免费| 91久久在线观看| 亚洲一区网站| 在线精品亚洲| 亚洲午夜久久久| 在线观看亚洲a| 国产精品二区二区三区| 亚洲一区二区三区免费在线观看| 蜜臀99久久精品久久久久久软件| 欧美激情按摩在线| 久久久久99| 国产精品久在线观看| 亚洲黄网站在线观看| 国产亚洲一区二区三区在线观看| 亚洲黄色av一区| 国产九区一区在线| 一本高清dvd不卡在线观看| 亚洲欧洲一区| 国产午夜精品一区二区三区视频| 亚洲高清不卡一区| 免费永久网站黄欧美| 国产精品免费网站| 亚洲欧美国产高清va在线播| 亚洲欧美激情视频在线观看一区二区三区| 欧美怡红院视频| 老牛影视一区二区三区| 欧美高清在线观看| 99这里只有精品| 国产精品日韩精品欧美在线| 亚洲小少妇裸体bbw| 欧美一区二视频| 日韩视频亚洲视频| 国产精品免费福利| 久久九九99视频| 久久天堂国产精品| 国产女精品视频网站免费| 宅男66日本亚洲欧美视频| 亚洲美女色禁图| 欧美视频在线观看免费网址| 欧美专区在线观看| 欧美成人a∨高清免费观看| 欧美调教视频| 久久xxxx精品视频| 最新日韩精品| 亚洲天堂av高清| 国产亚洲人成网站在线观看| 亚洲欧美一级二级三级| 国产精品wwwwww| 亚洲欧美美女| 国产日产精品一区二区三区四区的观看方式| 亚洲欧美国产视频| 久久精品国产亚洲精品| 亚洲视频在线观看| 亚洲欧美日韩国产一区| 一区二区三区视频免费在线观看| 欧美午夜免费| 欧美日韩综合另类| 国产精品vvv| 国户精品久久久久久久久久久不卡| 国产小视频国产精品| 欧美电影免费| 欧美 日韩 国产精品免费观看| 久久夜色精品| 欧美日韩一区二区在线播放| 国产精品亚洲人在线观看| 一区二区av在线| 一本一道久久综合狠狠老精东影业 | 欧美午夜电影网| 韩国一区二区在线观看| 在线观看日韩国产| 一区二区三区在线免费视频| 国产精品最新自拍| 亚洲国产一二三| 一区二区三区欧美| 农夫在线精品视频免费观看| 午夜精品一区二区在线观看| 亚洲欧美三级伦理| 亚洲女爱视频在线| 亚洲精品影视| 久久久久久久久久码影片| 久久亚洲欧洲| 国产乱理伦片在线观看夜一区 | 欧美黄色一级视频| 欧美韩国日本一区| 国产日韩一区二区三区| 亚洲国产欧美一区二区三区同亚洲| 欧美亚洲综合在线| 亚洲欧美日韩中文在线制服| 欧美日韩一二三区| 亚洲精品孕妇| 日韩一级视频免费观看在线| 麻豆精品传媒视频| 亚洲国产高清视频| 亚洲黄色影院| 欧美日韩视频一区二区| 亚洲午夜精品网| 欧美激情一区二区三区不卡| 免费观看成人www动漫视频| 亚洲国产高清一区二区三区| 亚洲三级网站| 欧美日韩一级片在线观看| 日韩天堂在线观看| 亚洲天堂第二页| 国产精品一区在线观看| 欧美一区二区在线观看| 欧美亚洲日本国产| 在线免费观看欧美| 亚洲一区免费| 中文国产成人精品| 亚洲一区二区高清| 国内精品久久久久久久影视麻豆| 久久这里只精品最新地址| 欧美日韩亚洲一区| 欧美风情在线| 欧美精品97| 午夜一区二区三区不卡视频| 久久视频在线视频| 午夜欧美大片免费观看| 国产精品成人一区| 亚洲东热激情| 国产亚洲一区在线| 亚洲高清二区| 欧美1级日本1级| 亚洲欧美成人网| 日韩一级大片|