C++ Template Metaprogramming 第九章試譯: Crossing the Compile-Time/Runtime Boundary (1)

來源:互聯網
上載者:User
(看過了這麼多編譯期演算法之後)還記得運行時吧?我們已經在編譯期的天空飛行了好久,現在是時候腳踏實地了。一個有趣的程式終究還是要在運行時幹點什麼的。本章就是關於怎樣穿越C++編譯期和運行時的邊界——這一層“臭氧層”,如果你想要的話——這樣我們的元程式可以真正的使用者面前施展拳腳。在C++中,進行這趟旅程的辦法恐怕是無窮無盡,但是其中有一些更加有用,下面講到的就是最常用的一些技巧。

9.1 for_each

STL裡最簡單的演算法在MPL裡面也應該有,也的確有。先回顧一下:std::for_each 遍曆一個(運行時的)序列,並且對每一個元素調用某個(運行時的)仿函數。類似的,mpl::for_each 遍曆一個編譯期的序列,並且調用運行時仿函數。儘管 std::for_each 是完全運行時的,mpl::for_each 卻是一個混合體,橫跨在編譯期和運行時世界之間。


為什麼用運行時的仿函數?
如果你在想,為什麼 mpl::for_each 要採用一個運行時的仿函數,而不是一個元函數呢?那麼考慮一下這個:一般來說,std::for_each 用到的仿函數返回 void,即便它真的返回一個什麼值,那個值也是被忽略的。換言之,如果這個仿函數真的要做點什麼事情,那它一定要以某種方式修改程式的狀態。然而,函數式編程 (functional programming) 是天生無狀態的,而元編程是函數式編程,所以對一個序列的每個元素調用某個元函數並沒有多大意義,除非我們要對返回的結果做點什麼。


9.1.1 類型列印 (Type Printing)

你想過要列印你的類型序列 (type sequences) 的內容嗎?如果我們用的編譯器可以輸出可讀的 std::type_info::name ,那我們就可以這麼幹:

struct print_type
{
  template <class T>
  void operator()(T) const
  {
    std::cout << typeid(T).name() << std::endl;
  }
};

typedef mpl::vector<int, long, char*> s;
int main()
{
  mpl::for_each<s>(print_type());
}

這段代碼有幾個東西值得你注意:首先,print_type 的 operator() 是個模板,因為它必須能處理出現在我們的序列中的任何類型。除非你要處理的序列的所有元素都能轉化成某一個類型,否則 mpl::for_each 的仿函數就必須有一個模板化的 operator() 。

其次,注意 for_each 傳給仿函數的是其每個類型的一個值初始化 (value-initialized) 對象。這種形式在序列元素都是整數常量 wrapper 的時候非常方便。然而,對別的類型就要小心了,如果那個元素是個引用,或者是沒有預設建構函式的類型,又或者乾脆就是個 void,這個演算法就沒辦法編譯,因為它們都不能被值初始化。

我們可以避免這個問題,只要加上一個小小的 wrapper:

template <class T>
struct wrap {};

// 包含引用
typedef mpl::vector<int&, long&, char*&> s;
 
mpl::for_each<
  mpl::transform<s, wrap<_1> >::type
>(print_type());

我們也要調整仿函數的簽名,讓它能接受新的參數:

struct print_type
{
  template <class T>
  void operator()(wrap<T>) const  // 推匯出 T
  {
    std::cout << typeid(T).name() << std::endl;
  }
};

由於這個用法如此常見,MPL提供了第二種形式的 for_each ,它接受一個轉換元函數作為第二參數。採用這種形式,我們就不用自己 wrap 序列了:

mpl::for_each<s, wrap<_1> >(print_type());

對 s 中的每一個元素 T,print_type 都被調用,參數是 wrap<T>。

9.1.2 類型訪問 (Type Visitation)

如果需要更加通用的方法來解決“函數調用邊界上的”類型問題,我們可以採用 Vistor 模式。

struct visit_type   //generalized visitation function object
{
  template <class Visitor>
  void operator()(Visitor) const
  {
    Visitor::visit();
  }
};

template <class T>  //specific visitor for type printing
struct print_visitor
{
  static void visit()
  {
    std::cout << typeid(T).name() << std::endl;
  }
};

int main()
{
  mpl::for_each<s, print_visitor<_1> >(visit_type());
}

在這裡,仿函數 visit_type 期望它的參數類型擁有一個 static 成員函數 visit ,我們可以讓 visitor 對象做任何事情。相對於前面的 for_each 例子來說,這是一個很小的變化,但是注意:print_visitor::visit 從來就沒有接受一個真正的 T 類型的對象,相反地,對於序列中的每一個 T,for_each 都把一個 print_visitor<T> 類型的執行個體傳遞給 visit_type,而關於 T 的類型資訊是在 print_visitor 的模版參數裡面傳送的。


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.