日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Template Metaprogramming

發布時間:2025/3/15 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Template Metaprogramming 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Template Metaprogramming 1. 何謂 Metaprogramming?
  • Metaprogram: program that manipulates another program.
  • Metaprogramming is not a new concept:
    • Compiler is a metaprogram: manipulates your code and produces code in a lower level code
    • Preprocessor
    • YACC
這個容易理解,就像 Metadata 是“關于數據的數據”一樣,Meta 這個前綴本身是很能說明問題的,如果把編譯器也看成是一個 Metaprogram ,那么這個概念的確是沒有什么神秘的,計算機里面的絕大多數程序,干的事情都不外乎把這種數據變成另外一種數據。

2. Metaprogramming 的幾種途徑
  • One approach: external to the language that is being manipulated
  • Another approach: Domain language and host language are the same
第一種就是說的 YACC之類的了,當然,命令解釋器也當仁不讓的可以算是。
第二種么,Preprocessor 當然算是了,template 則是重頭戲了。

3. 舉幾個簡單的例子先?
CppTM 領域最經典最簡單的例子莫過于計算階乘了,它簡單而有用,同時體現了 CppTM 的遞歸本質。我想絕大多數人當年學習遞歸的時候也是從這個例子開始的:

#include <iostream>

using namespace std;

template <int N>
struct factorial
{
??? static const int value = N * factorial<N - 1>::value;
};

template<>
struct factorial<0>
{
??? static const int value = 1;
};

int main()
{
??? cout << "factorial<10>: " << factorial<10>::value << endl;
??? cout << "sizeof char[factorial<4>::value]: " <<
??????????? sizeof(char[factorial<4>::value]) / sizeof(char) << endl;
}

輸出:

factorial<10>: 3628800
sizeof char[factorial<4>::value]: 24

這只是一個回顧,上面的簡單程序就不用我解釋了吧?下面的這個 remove_cv 算法會去掉參數類型的 const 和 volatile 修飾符(如果有的話),Boost type_traits 就是這么干的。

#include <iostream>

using namespace std;

template <class T> struct remove_cv
{ typedef T type; };

template <class T> struct remove_cv<const volatile T>
{ typedef T type; };

template <class T> struct remove_cv<const T>
{ typedef T type; };

template <class T> struct remove_cv<volatile T>
{ typedef T type; };

int main()
{
??? cout << "remove_cv<const int>: "
???????? << typeid(remove_cv<const int>::type).name() << endl;
??? cout << "remove_cv<volatile int>: "
???????? << typeid(remove_cv<volatile int>::type).name() << endl;
??? cout << "remove_cv<const volatile int>: "
???????? << typeid(remove_cv<const volatile int>::type).name() << endl;
}

輸出:

remove_cv<const int>: int
remove_cv<volatile int>: int
remove_cv<const volatile int>: int

這個也很簡單,但是非常有用。最后再來一個,相當有用的,它讓我們可以在編譯期間把一個數字作為二進制數來解釋:

#include <iostream>

using namespace std;

template <unsigned long N>
struct binary
{
??? static unsigned const value =
??????? binary<N/10>::value << 1 | N%10;
};

template <>
struct binary<0>
{
??? static unsigned const value = 0;
};

int main()
{
??? cout << "binary<1>: " << binary<1>::value << endl;
??? cout << "binary<11>: " << binary<11>::value << endl;
??? cout << "binary<101>: " << binary<101>::value << endl;
??? cout << "binary<111>: " << binary<111>::value << endl;
??? cout << "binary<1011101>: " << binary<1011101>::value << endl;
}

輸出:

binary<1>: 1
binary<11>: 3
binary<101>: 5
binary<111>: 7
binary<1011101>: 93

不過上面這個程序不容錯,換句話說,如果你寫 binary<123>::value ,編譯器不會阻止你,還會給出一個愚蠢的答案。如果要做一個容錯的解釋器,只需要玩一個小小的把戲(本人原創):

#include <iostream>

using namespace std;

namespace aux{
??? // 對于 0 和 1 以外的數,都不定義 value ,這樣在出現 0 1 之外的數
??? // 的時候,編譯器會抱怨找不到 value
??? template <unsigned long N>
??? struct binary
??? {};
???
??? template <>
??? struct binary<1>
??? { static unsigned const value = 1; };
???
??? template <>
??? struct binary<0>
??? { static unsigned const value = 0; };
}

template <unsigned long N>
struct binary
{
??? static unsigned const value =
??????? binary<N/10>::value << 1 | aux::binary<N%10>::value;
};


template <>
struct binary<0>
{
??? static unsigned const value = 0;
};

int main()
{
??? cout << "binary<1>: " << binary<1>::value << endl;
??? cout << "binary<11>: " << binary<11>::value << endl;
??? cout << "binary<101>: " << binary<101>::value << endl;
??? cout << "binary<111>: " << binary<111>::value << endl;
??? cout << "binary<1011101>: " << binary<1011101>::value << endl;
??? // 你可以 uncomment 下面這一行看看會發生什么
??? //cout << "binary<123>: " << binary<123>::value << endl;
}

輸出還是一樣,但是如果你寫了 binary<123>::value 這樣的東西,編譯器就會抱怨了:

error C2039: 'value' : is not a member of 'aux::binary<N>'
??????? with
??????? [
??????????? N=2
??????? ]
// bla bla bla

好了,例子夠多了,我們可以稍微總結一下。CppTM 的好處在于:
  • 把很多計算放到編譯期間完成,使得運行效率大為提高
  • 由于計算在編譯期間完成,很多錯誤也可以在編譯期間發現,程序員不用到了程序開始跑了才進入痛苦的調試
  • 有一些事情,比如 remove_cv ,在運行期間還的確不那么好做

  • 4. 我怎么開始 Metaprogamming 呢?
    在我們開始學習編程的時候,首先學到的是賦值、條件判斷、循環等等。在 CppTM 中,這些有了變化,如下:

    循環 --> 模板遞歸
    條件判斷 --> 模板偏特化
    賦值 --> 沒有,變量的值一旦確定就不會變化(這對于 functional programming 是常事)
    函數輸入輸出 --> 類型和常量

    其實這些特征并不是什么旁門左道,正好相反,它具有 functional programming 的特征,符合圖靈機模型(感興趣的話可以看這篇 paper: C++ Templates are Turing Complete)。

    5. 模板偏特化是個好東西,但是我每次都要把 if...then...else 映射成它,豈不是要累死?況且這編碼量也太大...
    這是個好問題,好在在計算機科學里面有一句箴言:You can solve everything by adding an extra layer of abstraction. 使用模板偏特化進行條件判斷如此常用,我們完全應該把它抽象出來以備重用:

    template <bool Cond, class Then, class Else>
    struct if_
    { typedef Then type; };

    template <class Then, class Else>
    struct if_ <false, Then, Else>
    { typedef Else type; };

    簡單吧? 雖然簡單,但是我們從此卻可以在更高的抽象層面上看問題,我們擺脫了用模板偏特化思考,現在可以直接用 if...then...else 來思考了。那么,上面解釋二進制數的程序就變成了下面這樣:

    #include <iostream>

    using namespace std;

    template <bool Cond, class Then, class Else>
    struct if_
    { typedef Then type; };

    template <class Then, class Else>
    struct if_ <false, Then, Else>
    { typedef Else type; };


    namespace aux{
    ??? struct one
    ??? { static unsigned const value = 1; };
    ??? struct zero
    ??? { static unsigned const value = 0; };
    ??? struct other
    ??? {};
    }

    template <unsigned long N>
    struct binary
    {
    ??? static unsigned const value =
    ??????? if_ <N/10 == 0, aux::zero, binary<N/10> >::type::value << 1 |
    ??????? if_ <N%10 == 0, aux::zero,
    ??????????? if_ <N%10 == 1, aux::one, aux::other>::type
    ??????? >::type::value;
    };

    int main()
    {
    ??? cout << "binary<1>: " << binary<1>::value << endl;
    ??? cout << "binary<11>: " << binary<11>::value << endl;
    ??? cout << "binary<101>: " << binary<101>::value << endl;
    ??? cout << "binary<111>: " << binary<111>::value << endl;
    ??? cout << "binary<1011101>: " << binary<1011101>::value << endl;
    ??? // 你可以 uncomment 下面這一行看看會發生什么
    ??? //cout << "binary<123>: " << binary<123>::value << endl;
    }

    輸出還是一樣,但是現在不僅程序長度減少,而且相關的邏輯也用我們熟悉的 if...then...else 的方式來表達。多一層抽象果然威力強大!當然,循環還是要用模板遞歸的。

    6. 流程控制的問題解決了,下面呢?我們是不是需要一些容器,像 STL 那樣?
    如果要表達一個裝“類型”的容器,你會怎么做?象下面這樣么?

    struct types
    {
    ??? typedef int t1;
    ??? typdef long t2;
    ??? typedef std::vector<double> t3;
    }

    大概只要稍微明智一點,你就會馬上放棄這個想法,它太沒有通用性了。真正的啟示來自于 Lisp ,在 Lisp 中,表是最重要的數據結構,幾乎是“萬物皆表”。一個表由一個頭和一個尾組成,可以嵌套,空表用 nil 表示。這種簡單的概念卻有著不可思議的表達能力。如果我們用 C++ 來模擬,就是這樣:

    template <class First, class Rest>
    struct cons
    {
    ??? typedef First first;
    ??? typedef Rest rest;
    };

    struct nil {};

    現在我們需要表示一個類型列表就有章可循了:

    typedef
    ??? cons<int,
    ??? cons<long,
    ??? cons<std::vector<double>,
    ??? nil> > > a_type_list;

    它是遞歸的,從而我們可以很容易的用遞歸的方式來對它們作協操作,用來操作它們的,就是 Metafunction,前面的 if_ 就是一個 Metafunction。例如我們想選擇兩個類型中比較大的一個,可以寫一個 choose_larger Metafunction:

    template <typename T1, typename T2>
    struct choose_larger
    {
    ??? typedef typename if_ <(sizeof(T1) > sizeof(T2)), T1, T2>::type type;
    };

    我們再次看到,由于有了 if_ ,我們的生活變得輕松多了。讓我們繼續向前發展,我們不想僅僅停留在兩個類型比大小上,我們希望選擇一個 type list 里面最大的那一個:

    template <typename T> struct largest;

    template <typename First, typename Rest>
    struct largest<cons<First, Rest> >
    ??? : choose_larger<First, typename largest<Rest>::type>
    {};

    template< typename First >
    struct largest<cons<First,nil> >
    { typedef First type; };

    其實上面的也可以用上 if_ ,只不過我們現在還沒有寫出判斷一個類型是否為 nil 的 Metafunction ,這是件很簡單的事情,大家可以自己去寫寫看。有了它們,得到一個 type list 里面最大的元素就輕而易舉了:

    #include <vector>
    #include <iostream>

    using namespace std;

    //... 上面的那些 Metafunction

    int main()
    {
    ??? typedef
    ??????? cons<int,
    ??????? cons<long,
    ??????? cons<std::vector<double>,
    ??????? nil> > > type_list;
    ???
    ??? cout << typeid(largest<type_list>::type).name() << endl;
    }

    輸出:

    class std::vector<double,class std::allocator<double> >

    7. 的確很棒,但是這跟我們當年學數據結構以后,沒事就寫個鏈表玩玩差不多,在生產環境中可不能這樣,用什么通用的方法么?
    終于到了這一步,在“重復發明輪子”足夠多次以后,終于就有人會出來發明通用輪子的,MPL 就是一個很好的嘗試,當然 Loki 也算是。
    還記得 if_ 讓我們嘗到的甜頭么?聰明人在看到了這些甜頭以后,是決不會止步不前的,Dave Abrahams 和 Aleskey Gurtovoy 就是這樣的聰明人,他們發明了 MPL 。留到下一篇好了。

    總結

    以上是生活随笔為你收集整理的Template Metaprogramming的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 超碰91在线 | 91全免费| 色综合天天综合综合国产 | 秋霞网一区二区三区 | 伊人精品一区二区三区 | 亚洲综合av一区二区三区 | 国产69精品久久 | 日本国产精品一区 | 日本一区视频在线观看 | 毛片av免费看 | 久久精品无码Av中文字幕 | 黄色一级视频免费观看 | 日b在线观看 | 中文字幕第12页 | 欧美高清| 国产婷婷在线观看 | 久久老熟女一区二区三区 | 亚洲成人资源 | www.亚洲色图 | 影音先锋中文在线 | 国产sm在线 | 亚欧成人精品一区二区 | 一本久久a精品一合区久久久 | 午夜视频欧美 | 国产无遮挡又黄又爽 | 成 年 人 黄 色 大 片大 全 | 香蕉视频传媒 | 女的高潮流时喷水图片大全 | 中文字幕一区视频 | 夜色一区 | 色999在线 | av免播放器 | 爱色成人网| 波多野结衣高清视频 | 羞羞免费视频 | 日韩成人影视 | 福利视频在线免费观看 | 人人干干 | 国产精品免费av一区二区三区 | 91pron在线| 欧美亚洲视频 | 日韩在线视频精品 | 欧美成人一区二区 | 久久av高潮av无码av喷吹 | 91中文字幕在线播放 | 亚洲黄网在线 | 九色九一| 欧美精品日韩少妇 | 欧美日韩五月天 | 人人做人人爱人人爽 | 精品国产aⅴ | 日本在线激情 | 影音先锋中文字幕在线 | 国产视频精品一区二区三区 | 国产免费一区二区三区免费视频 | 天天av网 | 久久综合激情网 | 一区二区免费播放 | 午夜精品久久久久久久久久久 | 国产一区二区三区中文字幕 | 免费在线观看污网站 | 伊人开心网 | 中文字幕av久久爽一区 | 丁香网五月天 | 日韩免费视频一区二区视频在线观看 | 精品人妻一区二区三区四区在线 | 日韩干| 亚洲成年 | 日本黄页网址 | 五十路六十路七十路熟婆 | 午夜老司机免费视频 | 先锋资源中文字幕 | 久色视频在线播放 | 不卡黄色 | 美女黄色小视频 | 男男在线观看 | 欧美日本韩国一区二区 | 无码内射中文字幕岛国片 | 亚洲天堂免费在线观看视频 | 国产成人免费av | 国产第一页屁屁影院 | 亚洲午夜伦理 | 国产精品欧美亚洲 | 人妻无码一区二区三区久久 | 天天av综合 | 天天插天天爽 | 亚洲石原莉奈一区二区在线观看 | 大奶在线播放 | av青娱乐| 中国一级片在线观看 | 免费看毛片网站 | 亚洲精品欧美在线 | 久久综合伊人77777麻豆最新章节 | 麻豆视频在线免费观看 | 黄色在线视频观看 | 韩国日本在线 | 337p色噜噜| 色婷婷av一区二区三区麻豆综合 | 免费a网|