• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Template Meta Programming入門から応用まで
 

Template Meta Programming入門から応用まで

on

  • 3,574 views

 

Statistics

Views

Total Views
3,574
Views on SlideShare
2,109
Embed Views
1,465

Actions

Likes
8
Downloads
23
Comments
0

4 Embeds 1,465

http://fimbul.hateblo.jp 1451
https://twitter.com 8
http://feedly.com 5
http://txt-txt.hateblo.jp 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Template Meta Programming入門から応用まで Template Meta Programming入門から応用まで Presentation Transcript

    • Template Meta Programming 入門から応用まで @fimbul11 2013/09/28 C++勉強会 @ tkb #2
    • 自己紹介 twitter : @fimbul11 github : fimbul University of Tsukuba B3  専攻 : 情報とメディア技術(mast) プログラミングとかやってます、歴は2年ちょっとぐらいです C++とかやってます、歴は1年半ぐらいです ペン回しとか出来ます、歴は7年ぐらいです
    • はじめに このような無名の勉強会に遠方からも多く の人に来ていただけました、本当にありが とうございます
    • おしながき 1. Templateの基本確認 2. Template Meta Programmingとは 3. Template Meta Programming入門 4. Template Meta Programming応用 5. その他付録 6. まとめ 7. 質疑応答
    • おしながき 1. Templateの基本確認 2. Template Meta Programmingとは 3. Template Meta Programming入門 4. Template Meta Programming応用 5. その他付録 6. まとめ 7. 質疑応答
    • Templateの基本確認 今日は ・C++未経験者 ・C++初心者 ・C++初心者(意味深) ・C++中級者及び上級者 ・歩くC++規格レベル ・人間C++コンパイラ など色々な方が居る可能性があるので
    • Templateの基本確認 今日は ・C++未経験者 ・C++初心者 ・C++初心者(意味深) ・C++中級者及び上級者 ・歩くC++規格レベル ・人間C++コンパイラ など色々な方が居る可能性があるので
    • Templateの基本確認 今日は ・C++未経験者 ・C++初心者 ・C++初心者(意味深) ・C++中級者及び上級者 ・歩くC++規格レベル ・人間C++コンパイラ など色々な方が居る可能性があるので
    • Templateの基本確認 今日は ・C++未経験者 ・C++初心者 ・C++初心者(意味深) ・C++中級者及び上級者 ・歩くC++規格レベル ・人間C++コンパイラ など色々な方が居る可能性があるので
    • Templateの基本確認 今日は ・C++未経験者 ・C++初心者 ・C++初心者(意味深) ・C++中級者及び上級者 ・歩くC++規格レベル ・人間C++コンパイラ など色々な方が居る可能性があるので
    • Templateの基本確認 今日は ・C++未経験者 ・C++初心者 ・C++初心者(意味深) ・C++中級者及び上級者 ・歩くC++規格レベル ・人間C++コンパイラ など色々な方が居る可能性があるので
    • Templateの基本確認 今日は ・C++未経験者 ・C++初心者 ・C++初心者(意味深) ・C++中級者及び上級者 ・歩くC++規格レベル ・人間C++コンパイラ など色々な方が居る可能性があるので
    • Templateの基本確認 ガチ勢の方はお手柔らかにお願いします
    • Templateの基本確認 初心者は知らないことがあるかも そうでない人も少しおさらいをしておく という意味で
    • Templateの基本確認 規格に基づいて文法などの Templateの基本を確認する  
    • Templateの基本確認 規格に基づいて文法などの Templateの基本を確認する むしろややこしいTemplateのルールについて 把握する事こそがTemplate Meta Programming 最大の山場といえます
    • Templateの基本確認 規格に基づいて文法などの Templateの基本を確認する 非常に重要な部分であり、templateを使った 経験が少ないとこれだけで十分すぎるぐら いつらいと思います
    • Templateの基本確認 ・C++11規格に沿う ・ただし細かい部分には触れない  (各自で規格を確認してほしい   自分もまる覚えはしていない) ・C++14~の機能(Variable Templates等)  はその他付録の章で扱う
    • Templateの基本確認 結構量があるので既に知ってる人は適当に 聞き流しつつ他の作業でもしていて下さい
    • Templateの基本確認 目次 1. Template is 何 2. テンプレートの文法 2-1. テンプレートパラメータ 2-2. クラステンプレート 2-3. 関数テンプレート 2-4. エイリアステンプレート 2-5. 可変長引数テンプレート 2-6. template template parameter
    • Templateの基本確認 目次 1. Template is 何 2. テンプレートの文法 2-1. テンプレートパラメータ 2-2. クラステンプレート 2-3. 関数テンプレート 2-4. エイリアステンプレート 2-5. 可変長引数テンプレート 2-6. template template parameter
    • Template is 何 テンプレートって何ですか?
    • Template is 何 テンプレートって何ですか? N3337見ましょう
    • Template is 何 14 Templates A template defines a family of classes or functions or an alias for a family of types.
    • Template is 何 テンプレートは クラスや関数、型エイリアスのファミリー を定義する
    • Template is 何 確認するまでもなかったかもしれません 次に文法を確認していくことにします
    • Templateの基本確認 目次 1. Template is 何 2. テンプレートの文法 2-1. テンプレートパラメータ 2-2. クラステンプレート 2-3. 関数テンプレート 2-4. エイリアステンプレート 2-5. 可変長引数テンプレート 2-6. template template parameter
    • テンプレートの文法 テンプレートの文法を確認する  
    • テンプレートの文法 テンプレートの文法を確認する 規格を参照して定義から見ていく
    • テンプレートの文法 テンプレートの文法を確認する template-declaration: template < template-parameter-list > declaration template-parameter-list: template-parameter template-parameter-list , template-parameter
    • テンプレートの文法 テンプレートの文法を確認する template-declaration: template < template-parameter-list > declaration template-parameter-list: template-parameter template-parameter-list , template-parameter
    • テンプレートの文法 テンプレートは template < template-parameter-list > declaration   として定義される
    • テンプレートの文法 テンプレートは template < template-parameter-list > declaration   として定義される
    • テンプレートの文法 template-parameter-listは template-parameter 又は template-parameter-list , template-parameter  として定義される
    • テンプレートの文法 template-parameter-listは template-parameter 又は template-parameter-list , template-parameter  として定義される
    • Templateの基本確認 目次 1. Template is 何 2. テンプレートの文法 2-1. テンプレートパラメータ 2-2. クラステンプレート 2-3. 関数テンプレート 2-4. エイリアステンプレート 2-5. 可変長引数テンプレート 2-6. template template parameter
    • Template parameters The syntax for template-parameters is: template-parameter: type-parameter parameter-declaration type-parameter: class ...opt identifieropt class identifieropt= type-id typename ...opt identifieropt typename identifieropt= type-id template < template-parameter-list > class ...opt identifieropt template < template-parameter-list > class identifieropt= idexpression optの後の要素は省略可能
    • Template parameters The syntax for template-parameters is: template-parameter: type-parameter parameter-declaration type-parameter: class ...opt identifieropt class identifieropt= type-id typename ...opt identifieropt typename identifieropt= type-id template < template-parameter-list > class ...opt identifieropt template < template-parameter-list > class identifieropt= idexpression optの後の要素は省略可能
    • Template parameters template-parameterは type-parameter又は parameter-declaration  である
    • Template parameters template-parameterは type-parameter又は parameter-declaration  である
    • type-parameter まずtype-parameterの定義の一部のみ紹介する type-parameter : class ...opt identifieropt class identifieropt= type-id typename ...opt identifieropt typename identifieropt= type-id が付いた要素は省略可能 opt 一見種類が多い
    • type-parameter まずtype-parameterの定義の一部のみ紹介する type-parameter : class ...opt identifieropt class identifieropt= type-id typename ...opt identifieropt typename identifieropt= type-id 対応する色のもの同士は実質同じ また = type-idの部分はデフォルト引数の有無
    • type-parameter 定義の数は一見多いが実際は結構単純 順番に定義の意味を見ていく
    • type-parameter この4つにおいてtype-parameterの頭の部分では classキーワードとtypenameキーワード は同じ意味 (他の場所では当然意味が違うので注意する、 C++には文脈によって同じキーワードに複数 の意味を持たせる文化がある) (例えばtemplate template parameterの場合違う)
    • type-parameter 最も単純なtype-parameterを使ったtemplate例 template<class> (又はtemplate<typename>) type-parameterのclassとtypenameキーワードは 今後の説明ではclassで統一する (スライドの都合上、短い方がありがたい)
    • type-parameter type-parameter : class ...opt identifieropt identifierはtype-parameterの名前 例 template<class Type>
    • type-parameter type-parameter : class ...opt identifieropt = type-id type-idがデフォルト引数になる 例 template<class Type = int> 何も渡されなければTypeはintになる
    • type-parameter 関数のデフォルト引数同様に 最初のデフォルト引数を持つパラメータ以降 の全パラメータにデフォルト引数が必要 例 template<class T1 = int, class T2 = int> class A;
    • type-parameter デフォルト引数の設定は複数に分けて書いて も統合される(N3337 305p) template<class T1, class T2 = int> class A; template<class T1 = int, class T2> class A;   は以下に等しい template<class T1 = int, class T2 = int> class A;
    • type-parameter 当然後ろの要素から順に設定する必要がある template<class T1 = int, class T2> class A; template<class T1, class T2 = int> class A; この順だと1行目でダメです error: no default argument for ‘T2’
    • type-parameter 例まとめ template<class> 無名 template<class = int> デフォルト引数有り template<class Type> 名前有り template<class Type = int> デフォルト引数有り template<class T1, class T2> 複数のパラメータ
    • type-parameter 別の定義は後の章で見るので ひとまずtype-parameterをこれで終わります
    • Template parameters template-parameterは type-parameter又は parameter-declaration  である
    • parameter-declaration parameter-declaration: attribute-specifier-seqopt decl-specifier-seq declarator attribute-specifier-seqopt decl-specifier-seq declarator = initializer-clause attribute-specifier-seqopt decl-specifier-seq abstractdeclaratoropt attribute-specifier-seqopt decl-specifier-seq abstractdeclaratoropt= initializer-clause
    • parameter-declaration parameter-declarationは non-type template-parameter  (非型テンプレートパラメータ)の場合に使う
    • non-type template-parameter 例を見た方が早い template<char C> template<int N> 渡される引数がnon-typeなtemplate-parameter int Nやchar Cがparameter-declarationにあたる
    • non-type template-parameter もっと具体的な例 template<  class T, // Tはまだ何型か分からない std::size_t N // Nはstd::size_t型のパラメータ > struct array; std::array<int, 10>の要素数10を受け取っている Nがnon-type template-parameter
    • non-type template-parameter 全ての型が non-type template-parameterに なれるわけではない
    • non-type template-parameter non-type template-parameterになれる条件(重要) 1. integral or enumeration type(実質整数型) 2. pointer to object or pointer to function 3. lvalue reference to object or lvalue reference to function 4. pointer to member  5. std::nullptr_t (実質ポインタ型)  (又はこれらにcv修飾子が付いたもの) のいずれかを満たす型であるとき
    • non-type template-parameter non-type template-parameterになれる条件は 要するに整数型かポインタ型か左辺値参照
    • non-type template-parameter 例 template<std::size_t N> std::size_tは整数型のtypedefなのでwell-formed class myClass {}; template<myClass Arg> 先程の条件を満たさないクラス等はill-formed
    • non-type template-parameter 例 N3337 p305 template<double d> class X; // Error 浮動小数点型は条件に一致しない template<double* pd> class Y; // OK pointer to objectなのでOK template<double& rd> class Z; // OK lvalue referenceなのでOK
    • non-type template-parameter 右辺値は値渡しで受け取る必要がある non-type template-parameterになれないrvalue referenceは論外だ し、const lvalue referenceでもダメ (どうせ渡せる型が小さいデータなのでコピーで然程問題ない) 例 template<int N> class X; using type = X<0>; // OK template<const int& N> class Y; using type = Y<0> // error: ‘0’ is not a valid template argument because it is not an lvalue
    • non-type template-parameter non-type template-parameterに渡せるものにも 条件がある
    • non-type template-parameter non-type template-parameterに渡せるもの ・定数式 ・関数のアドレス ・外部リンケージのあるオブジェクト ・静的クラス・メンバのアドレス
    • non-type template-parameter non-type template-parameterに渡せるもの 定数式(分類に不安があるが…) ・非型テンプレート引数(先程のNなど) ・コンパイル時定数(constexpr)  (後で説明する) ・sizeofやsizeof...など
    • non-type template-parameter non-type template-parameterに渡せるもの 関数のアドレス ・説明不要、f()などのアドレス
    • non-type template-parameter non-type template-parameterに渡せるもの 外部リンケージのあるオブジェクト ・extern修飾されたもの
    • non-type template-parameter non-type template-parameterに渡せるもの 静的クラス・メンバのアドレス ・説明不要、そのままの意味
    • template-parameter template-parameterに関しては、他にも細かい 事が大量に規定されているので適宜規格を調 べる必要がある
    • template-parameter template-parameterまとめ ・テンプレートパラメータには型パラメータ  と非型パラメータがある ・非型パラメータになるものや渡せるものは  限られている
    • Templateの基本確認 目次 1. Template is 何 2. テンプレートの文法 2-1. テンプレートパラメータ 2-2. クラステンプレート 2-3. 関数テンプレート 2-4. エイリアステンプレート 2-5. 可変長引数テンプレート 2-6. template template parameter
    • Class templates A class template defines the layout and operations for an unbounded set of related types.
    • Class templates クラステンプレートは、関連する型(要する に扱う要素の型が違うだけのクラス群)のレ イアウトをまとめて定義する
    • Class templates 例 汎用的な配列クラス N3337 P315 template<class T> class Array {  T* v; // Tは引数として渡された型に置き換わる int sz;  public:  explicit Array(int);  T& operator[](int);  T& elem(int i) { return v[i]; }  };   Array<int>やArray<char>は渡した型を扱える
    • Class templates クラスメンバテンプレート class Test { public: template<class T> T f(T arg) { return arg; } // 関数テンプレートは後で取り上 げる };   クラスメンバもテンプレートに出来る operator等もテンプレート化可能
    • Class templates 非型テンプレートパラメータを使う例 template<std::size_t N> struct integer { // structも当然テンプレート化可能 static constexpr std::size_t value = N; };  Nの型にconst等は付いていなくても非型テ ンプレートパラメータは(暗黙に)コンパイル 時に値が決まる定数として扱える (constexpr修飾された値の初期化にも使える)
    • Class templates 非型テンプレートパラメータを使う例 template<std::size_t N> struct integer { static constexpr std::size_t value = ++N; };  当然、非型テンプレートパラメータの型の 書き換えはill-formedである (定数なのでうっかり試みても失敗する) error: increment of read-only location ‘N’
    • Class templates 違法な例を招く行為(当然してはいけない) template<class T, T v> struct data { static constexpr T* get_address() { return const_cast<T*>(&static_cast<const T&>(v)); } };  このような行為は万死に値する
    • Class templates 補足事項 Template specializations  テンプレートの特殊化
    • Class templates Template specializations テンプレートの特殊化 template<class T = int> struct A { static int x; }; template<class U> void g(U) { } template<> struct A<double> {}; // specialize for T == double template<> struct A<> { }; // specialize for T == int template<> void g(char) { } // specialize for U == char テンプレートクラスやテンプレート関数のtype-parameterに ある値が入った状態のものは特殊化されているといえる
    • Class templates class template partial specializations クラステンプレートの部分特殊化
    • Class templates 例 template<class T1, class T2, int I> class A {}; // unspecialized template<class T, int I> class A<T, T*, I> {}; template<class T1, class T2, int I> class A<T1*, T2, I> {}; template<class T> class A<int, T*, 5> {}; template<class T1, class T2, int I> class A<T1, T2*, I> {};
    • Class templates template<class T1, class T2, int I> class A {}; // unspecialized template<class T, int I> class A<T, T*, I> {}; このように抽象的なテンプレートパラメータの数を減らし 部分的に具体的要素に置き換えた特殊化を作る事が出来る
    • Class templates template<class T1, class T2, int I> class A {}; // unspecialized template<class T, int I> class A<T, T*, I> {}; 部分特殊化されたテンプレートは、条件を満たす場合には 特殊化されていないテンプレートより優先的にマッチする
    • Class templates template<class T1, class T2, int I> class A {}; // unspecialized template<class T, int I> class A<T, T*, I> {}; // classA<int, int*, 0>な どの場合だけこちらの定義が使われる ある条件を満たす場合のみ実装を切り替える事が出来る これはTemplate Meta Programmingにおいて非常に重要
    • Class templates また、部分特殊化によっては template<int N> struct A; template<> struct A<0>; のようにtemplateの中身が空<>になることも あるが特に文法上問題無い
    • Class templates 勿論、型テンプレートパラメータだけでな く非型テンプレートパラメータも部分特殊 化出来る(例は後の章で登場する)
    • Class templates テンプレートの特殊化及び部分特殊化が出 来るケースにも条件がある
    • Class templates テンプレートの特殊化が出来るケース 特殊化できる OK : 名前空間スコープ NG : クラススコープ つまりクラスのメンバは特殊化不可能
    • Class templates テンプレートの部分特殊化が出来るケース 部分特殊化できる: OK : クラステンプレート NG : 関数テンプレート 部分特殊化は非クラスメンバのクラステンプ レート限定の機能といえる
    • Class templates Class templatesまとめ ・クラステンプレートは、 関連する型のレ  イアウトをまとめて定義する ・名前空間スコープのクラステンプレートの  み部分特殊化によって実装を切り替え可
    • Templateの基本確認 目次 1. Template is 何 2. テンプレートの文法 2-1. テンプレートパラメータ 2-2. クラステンプレート 2-3. 関数テンプレート 2-4. エイリアステンプレート 2-5. 可変長引数テンプレート 2-6. template template parameter
    • Function templates A function template defines an unbounded set of related functions.
    • Function templates 関数テンプレートは、関連する関数(要する に扱う要素の型が違うだけの関数群)をまと めて定義する
    • Function templates 例 template<class T> T twice(T arg) { return arg * 2; } twice(2); // 4 twice(2.5); // 5.0 整数も浮動小数点も扱える
    • Function templates Template argument deduction テンプレート引数の型推論
    • Function templates 先程の例 twice(2); twice(2.5); twice<int>(2)と書かなくとも適切にint版の twiceが呼ばれ、twice<double>(2.5)と書かな くとも適切にdouble版のtwiceが呼ばれた
    • Function templates このような引数からテンプレートパラメー タ型を推論する機能が Template argument deduction
    • Function templates 幾つかの例外を除き、渡した型がそのまま 推論される
    • Function templates 例外1 ・配列型(T[N])はポインタ(T*)に変換される ・関数型(R(Args...))は関数ポインタ  (R(*(Args...))に変換される
    • Function templates 例外2 ・値渡しの場合のみトップレベルのcv修飾子  が無視される
    • Function templates 例外2 template<class T> T test(T arg) { return arg; } int a = 0; test(a); // Tはint型 const int b = 0; test(b); // Tはint型
    • Function templates 例外2 このような挙動の理由は、値コピーを取る場合、結局元の オブジェクトの値が変更される可能性が無いためとされる const int b = 0; test(b); // (testの中ではbのコピーが扱われるのでbの値自体 が変えられることはない、じゃあコピーするときはconst無 視してもいいんじゃね)…とコンパイラは思う
    • Function templates 例外2 constと同様にvolatileも無視される
    • Function templates 例外2 よって値を変更する可能性のある参照やポ インタ渡しの場合はcv修飾子も保持される template<class T> T test(T& arg) { return arg; } const int b = 0; test(b); // Tはint& const型
    • Function templates 例外2 ただし明示的に渡せば、渡した通りになる const int b = 0; test<const int>(b); // Tはconst intになる
    • Function templates 例外2 cv修飾子が無視される可能性があるのは トップレベルの型のみである 例えばvector<const int>などのconst int部分は トップレベルではないので無視されない
    • Function templates 当然非型テンプレートも推論してくれる template<class T, T N> constexpr T get(std::integral_constant<T, N>) { return N; } std::cout << get(std::integral_constant<int, 0>()) << std::endl; // T = int, N = 0と推論され、0が出力される
    • Function templates std::integral_constant<T, N> のようにテンプレートクラスのテンプレー トパラメータとしてネストされた内部の型 でもTやNとして推論出来る
    • Function templates Template argument deductionの規格を厳密に 読んで全てのルールを把握していくだけで尋 常じゃない大変さなので残りは省略します
    • Function templates Function template overloading 関数テンプレートのオーバーロード
    • Function templates 部分特殊化は出来ないが特殊化は出来る これらはオーバーロードされる template<class T> void output(T val); template<> void output(const std::string& val);
    • Function templates オーバーロードの解決に関しては省略する N3337 14.5.6.2 Partial ordering of function templates  を参照して欲しい ambiguousになる例など取り上げられている
    • Function templates Function templatesまとめ ・関数テンプレートは引数から型を推論し  てくれる ・特殊化、オーバーロードが可能である
    • Templateの基本確認 目次 1. Template is 何 2. テンプレートの文法 2-1. テンプレートパラメータ 2-2. クラステンプレート 2-3. 関数テンプレート 2-4. エイリアステンプレート 2-5. 可変長引数テンプレート 2-6. template template parameter
    • Alias templates 1. A template-declaration in which the declaration is an alias-declaration declares the identifier to be a alias template. An alias template is a name for a family of types. The name of the alias template is a template-name.
    • Alias templates 2. When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the templateparameters in the type-id of the alias template.
    • Alias templates もはや訳すのも面倒なので実例を見る
    • Alias templates template <class T> using add_pointer = typename std::add_pointer< T >::type; add_pointer<T>はstd::add_pointer< T >::typeと同じ型になる
    • Alias templates 別にtemplateである必要は無い using type = int;  typeはint型の別名として使える、簡単!
    • Alias templates typedef int integer; using integer = int; は同じ効果 typedefの代わりとして使える (ただし使えるのはC++11以降)
    • Alias templates Template Meta Programmingではtypenameと か::typeを書かなくて済むようにする為に 使ったりする template<class T> using add_p = typename std::add_pointer<T>::type; add_p<int>; // int*
    • Alias templates 余談 typedefは指定子である 指定子の順番に意味は無い typedef int Integer1; (こっちが多分メジャー) int typedef Integer2; (稀に見かける) // どちらもOK int Integer3 typedef;   // これはダメ
    • Alias templates typedef int Integer1; のように どうしてtypedef A B;の形がメジャーなのか?
    • Alias templates 次のような例を考える int typedef type; はOKだが int* typedef type; はダメ 「*」は、指定子(specifier)ではなく、宣言子 (declarator)のため
    • Alias templates 結論 多分、typedefを最初に書かない場合には宣 言子が出てきた時に面倒臭いから
    • Alias templates 後方互換性を気にしなくていいなら正直 typedefよりusingを使うと良いと思います (個人的にはusingの方が好き)
    • Alias templates 余談 template structなどの状態のメタ関数では遅延されるタイミ ングで正格評価される場合がある?(エイリアステンプレー トにしたところコンパイル時間が大幅に伸びた経験がある)
    • Alias templates Alias templatesまとめ ・usingを使う事で型の別名として使える ・typedefと違いテンプレート化出来る
    • Templateの基本確認 目次 1. Template is 何 2. テンプレートの文法 2-1. テンプレートパラメータ 2-2. クラステンプレート 2-3. 関数テンプレート 2-4. エイリアステンプレート 2-5. 可変長引数テンプレート 2-6. template template parameter
    • Variadic templates 1. A template parameter pack is a template parameter that accepts zero or more template arguments.
    • Variadic templates 2. A function parameter pack is a function parameter that accepts zero or more function arguments.
    • Variadic templates つまり、 ・可変長引数を取るクラス ・可変長引数を取る関数 の為の可変長引数テンプレート
    • Variadic templates The syntax for template-parameters is: template-parameter: type-parameter parameter-declaration type-parameter: class ...opt identifieropt class identifieropt= type-id typename ...opt identifieropt typename identifieropt= type-id template < template-parameter-list > class ...opt identifieropt template < template-parameter-list > class identifieropt= idexpression optの後の要素は省略可能
    • Variadic templates 先程Template parametersで飛ばした ...opt  を使う また、「...」を規格上ellipsisと呼ぶ 先の定義通りデフォルト引数は取れない
    • Variadic templates template<class... Types> struct Tuple {};  class...の部分は可変長引数テンプレート その名前はTypes
    • Variadic templates template<class... Types> struct Tuple {}; 可変長引数テンプレートは0個以上の引数を 好きなだけ受け取れる 勿論、非型テンプレート引数版も可能 template<int... Indices> struct Numbers {};
    • Variadic templates template<class... Types> struct Tuple {}; 例 Tuple<int, double, char, void*>  このように好きなだけ渡せる template<int... Indices> struct Numbers {}; Numbers<1,2,3,4,5>
    • Variadic templates このような可変長引数部分の事を template parameter pack  と呼ぶ
    • Variadic templates 例 template<class... Types> struct List; template<class Type, Type... Args> struct Data; TypesやArgsはTemplate Parameter Pack
    • Variadic templates 可変長引数テンプレートは基本的には、 template parameterのうち最後でなければな らない(一部例外がある) template<class Type, class... Types> // OK template<class... Types, class Type> // NG
    • Variadic templates 例外 template<std::size_t... Indices1, std::size_t... Indices2> void f(index_tuple<Indices1...>, index_tuple<Indices2...>); ・関数の引数の型が持つパラメータパック  を推論する場合 ・特に可変長引数を持つ型の引数を複数受  け取る場合には複数のパラメータパック  を持つことが出来る
    • Variadic templates pack expansion  受け取ったパラメータパックを展開する
    • Variadic templates 可変長引数として受け取ったテンプレート パラメータパックを使う為には それを展開する必要がある
    • Variadic templates 展開はpattern and an ellipsisからなる
    • Variadic templates template<class ResultType, class... Types> ResultType sum(const Types&... Args); sum<int>(1,2,3,4,5,6,7,8,9,10); 可変長の関数テンプレートにおいて型推論を行い、その結 果をTypesに格納して受け取っている
    • Variadic templates template<class Type, class... Types> Type sum(const Types&... Args); におけるconst Types&...がパック展開 class... Typesはsum<int>(1,2,3,4,5,6,7,8,9,10); なのでTypesはint, int, int, int, int, int, int, int, int, int に推論される
    • Variadic templates このときTypes...は int, int, int, int, int, int, int, int, int, int  に展開される
    • Variadic templates patternはパック展開時に処理を施す const Types&...はTypesの受け取った型1つ1つ にconst修飾及び参照&を付加した型になる よってconst Types&...は const int&, const int&, const int&, const int&, const int&, const int&, const int&, const int&, const int&, const int&  に展開される
    • Variadic templates 結果として template<class ResultType, class... Types> ResultType sum(const Types&... Args);は sum<int>(1,2,3,4,5,6,7,8,9,10);のとき int sum<int>(const int&, const int&, const int&, const int&, const int&, const int&, const int&, const int&, const int&, const int&); と等価になっている
    • Variadic templates 同様にして関数の引数もparameter packとし て受け取っている template<class ResultType, class... Types> ResultType sum(const Types&... Args);
    • Variadic templates sum<int>(1,2,3,4,5,6,7,8,9,10);のとき Args...は1,2,3,4,5,6,7,8,9,10になる
    • Variadic templates 例として受け取った数の総和を計算する関数を挙げる template<class Type, class... Types> Type sum(const Types&... Args) { // parameter pack Type result = 0; for(auto& elem : { static_cast<Type>(Args)... }) // pack expansion { result += elem; } return result; } int main() { std::cout << sum<int>(1,2,3,4,5,6,7,8,9,10) << std::endl; // 55 }
    • Variadic templates この例ではinitializer-listに対して { static_cast<Type>(Args)... } を展開することで {static_cast<Type>(1), static_cast<Type>(2), static_cast<Type>(3),  中略, static_cast<Type>(10)} を生成している 結果として今回は全要素がint型の{1,2,3,4,5,6,7,8,9,10}になる
    • Variadic templates sum<int>(1,2.0,3.5,4,5,6,7,8,9,10); のように型が異なる要素が混入していてもキャストしながら 展開する事が出来る
    • Variadic templates 補足 template parameter packの要素数を得るには sizeof...を使う sizeof...(Types) sizeof...(Args) sizeof...に対してはパックは展開せずに渡す sizeof...(Types...) // NG
    • Variadic templates 余談 ピリオドが6つ連続するコードが存在し得る (多分Sprout等で稀にお目にかかれる) 例 template<class... T> void f(T......); // なにこれキモイ
    • Variadic templates これは前半3つのピリオドがVariadic Templatesの展開、後半3つ のピリオドがC言語スタイルの可変長引数の...であり、真ん中 のカンマが省略されたもの template<class... T> void f(T..., ...); // ただのellipsis, ellipsisにすぎない に等しいのでビビる必要は無い
    • Variadic templates Variadic templatesまとめ ・Variadic TemplatesはTemplate Parameter Packと  して受け取る ・型パラメータ、非型パラメータを扱える ・型パラメータは関数の引数の型として利用出来る ・非型パラメータは関数の引数として利用出来る ・Template Parameter Packはpattern and ellipsis  によって展開してから使う
    • Templateの基本確認 目次 1. Template is 何 2. テンプレートの文法 2-1. テンプレートパラメータ 2-2. クラステンプレート 2-3. 関数テンプレート 2-4. エイリアステンプレート 2-5. 可変長引数テンプレート 2-6. template template parameter
    • Template Template Parameters The syntax for template-parameters is: template-parameter: type-parameter parameter-declaration type-parameter: class ...opt identifieropt class identifieropt= type-id typename ...opt identifieropt typename identifieropt= type-id template < template-parameter-list > class ...opt identifieropt template < template-parameter-list > class identifieropt= idexpression optの後の要素は省略可能
    • Template Template Parameters template <class Container> class hoge;  では hoge<std::vector<int>> h;  は出来ても hoge<std::vector> h;  は出来ない
    • Template Template Parameters 要するにテンプレートクラス自体などを受 け取る為のテンプレートパラメータ
    • Template Template Parameters アキラさんのブログに分かりやすい例が あったので引用する template <class T> class vector; template <template<class> class Container> class hoge { Container<int> c; }; hoge<vector> h; // OK
    • Template Template Parameters template < template-parameter-list > class ...opt identifieropt template < template-parameter-list > class identifieropt= idexpression このようにして受け取れる optにellipsisがあるように可変長引数でも可能
    • Template Template Parameters Template Template Parametersまとめ ・テンプレートクラス等を受け取る場合に  使う為のTemplate Parameter
    • おしながき 1. Templateの基本確認 2. Template Meta Programmingとは 3. Template Meta Programming入門 4. Template Meta Programming応用 5. その他付録 6. まとめ 7. 質疑応答
    • Template Meta Programmingとは お疲れ様でした、テンプレートの機能をひ と通り確認するだけで絶望的な量でした これでも相当、本当に沢山端折って説明し たので規格自体はこの数倍の量があります
    • Template Meta Programmingとは がっかりしたところで、初心者も最低限の 知識が付いたと思うので二章に入ります
    • Template Meta Programmingとは Template Meta Programmingとは何か
    • Template Meta Programmingとは テンプレートを利用して、型や値に関する 演算・処理をコンパイル時に行う技法 よく省略してTMPと呼ばれる
    • Template Meta Programmingとは テンプレートはコンパイル時に処理されるの でこの技法ではコンパイル時に計算が出来る
    • Template Meta Programmingとは この技法を用いた機能が標準ライブラリに もtype_traitsをはじめ多く含まれている
    • Template Meta Programmingとは 大きく分けて ・型を処理するもの ・値(として型)を処理するもの に分けられる
    • Template Meta Programmingとは 型を処理するものはたいてい template<class T>  のような型パラメータを使って型を操作して いく
    • Template Meta Programmingとは 値(として型)を処理するものはたいてい template<int N>  のような非型パラメータを使って値(として 使えるような型)を操作していく
    • Template Meta Programmingとは 次章で実例を見ていくとしましょう
    • おしながき 1. Templateの基本確認 2. Template Meta Programmingとは 3. Template Meta Programming入門 4. Template Meta Programming応用 5. その他付録 6. まとめ 7. 質疑応答
    • Template Meta Programming入門 この章では実例を見ていく 初心者向けに最も単純な例から取り上げる
    • Template Meta Programming入門 例1 型を受け取って値を返すもの
    • Template Meta Programming入門 例えばある2つの型を比較して 同じ型であればtrue 異なる型であればfalse を得たい
    • Template Meta Programming入門 そのような機能を提供する std::is_same が標準ライブラリにはある
    • Template Meta Programming入門 これは以下のように使う std::is_same<int, int>::value // true std::is_same<int, unsigned int>::value // false
    • Template Meta Programming入門 実装はどうなっているのか
    • Template Meta Programming入門 宣言 template<class T, class U> struct is_same;  ただ2つの型を受け取る
    • Template Meta Programming入門 問題はどうやって判別して結果を変えるか
    • Template Meta Programming入門 答え : 部分特殊化を使う
    • Template Meta Programming入門 template<class T, class U> // different types struct is_same { static constexpr auto value = false; }; template<class T> // same type struct is_same<T, T> { static constexpr auto value = true; };
    • Template Meta Programming入門 template<class T> struct is_same<T, T> { static constexpr auto value = true; }; 両辺に同じ型が渡された場合のみこちらの 定義が使われる
    • Template Meta Programming入門 条件分岐 → 部分特殊化を使う
    • Template Meta Programming入門 結果の返し方 静的なメンバ定数を定義する static constexpr auto value = true; // ただの静的定数でも良い そうすれば、 is_same<int, int>::valueのようにして得られる
    • Template Meta Programming入門 このような実質関数として働くようなテン プレートクラスはmeta functionと呼ばれる
    • Template Meta Programming入門 例2 値を受け取って型を返すもの
    • Template Meta Programming入門 条件式によって違う型を返すメタ関数
    • Template Meta Programming入門 std::conditionalと同じ働きのメタ関数 template<bool B, class T, class F> // true struct conditional { using type = T; }; template<class T, class F> // false struct conditional<false, T, F> { using type = F; }; 型を返す場合はメンバ型を定義してやる typename conditional<true, int, char>::type // int
    • Template Meta Programming入門 例3 非型テンプレートパラメータによる演算
    • Template Meta Programming入門 template<int X, int Y> struct add { static constexpr auto value = X + Y; }; add<1, 2>::value // 3 非型パラメータを用いてコンパイル時に演算
    • Template Meta Programming入門 発展例 階乗の計算
    • Template Meta Programming入門 template<int i> struct fact { static constexpr auto value = i * fact<i - 1>::value; }; template<> struct fact<1> // 1の場合 { static constexpr auto value = 1; }; 階乗の計算ではvalueの定義にfact<i - 1>::valueを利用するこ とで再帰を実現する
    • Template Meta Programming入門 template<> struct fact<1> // 1の場合 { static constexpr auto value = 1; }; 1の部分特殊化が無いと再帰が止まらなくなる事に注意する
    • Template Meta Programming入門 注意 template<int i> struct fact { static constexpr auto value = i > 1? i * fact<i - 1>::value : 1; // 再帰が止まらない }; 三項演算子はtrueの場合も両辺の型の等価性を確認するた めに両辺ともインスタンス化しようとするので、再帰が止 まらない(trueでもfact<i - 1>::valueをインスタンス化する)
    • Template Meta Programming入門 余談 インスタンス化を遅らせて出来る限り型のまま処理するこ とで部分特殊化無しで実装出来る場合もある template<std::size_t i> struct fact { static constexpr auto value = i * std::conditional< (i > 1), fact<i - 1>, std::integral_constant<std::size_t, 1> >::type::value; };  これは再帰が止まる conditionalの両辺をメタ関数にして遅延評価させる
    • Template Meta Programming入門 まとめ1 メタ関数には単純な例だけでも 入力として ・値を受け取るもの ・型を受け取るもの 出力として ・値を返すもの ・型を返すもの がある
    • Template Meta Programming入門 まとめ2 条件分岐 ・クラステンプレートの部分特殊化を使う (後でenable_if等も取り上げる) 結果の返し方 ・型を返すならばメンバ型を定義する ・値を返すならば静的メンバ定数を定義する 名前は慣習として型であればtype、値であればvalueを使う
    • Template Meta Programming入門 補足 コンパイル時にエラーチェックさせる
    • Template Meta Programming入門 配列の要素数は正の値であるべきだ template<class T, std::size_t N, bool = (N > 0)> struct array; // 実装無し template<class T, std::size_t N> struct array<T, N, true> { // 実装 }; array<int , 0> a; // 実装が無いのでコンパイルエラーになる このような不正値に対して意図的にコンパイルエラーに出 来るようなコードを書く技法は良く使われる
    • Template Meta Programming入門 template<class T, std::size_t N, bool = (N > 0)> 比較演算子は括弧で囲む必要がある std::vector<std::vector<int>>のようなネストされたテンプ レートが>>演算子に解釈される問題はC++11以降では平気
    • Template Meta Programming入門 補足 static_assert
    • Template Meta Programming入門 コンパイル時アサート static_assert(fact<6>::value == 720, ""); コンパイル時に計算可能な値に対して用いる trueであれば無出力 falseであればコンパイルエラーになる 1番目の引数に条件式 2番目の引数にエラー時のメッセージを渡す 当然、実行時計算されるような値を渡すと失敗扱い
    • おしながき 1. Templateの基本確認 2. Template Meta Programmingとは 3. Template Meta Programming入門 4. Template Meta Programming応用 5. その他付録 6. まとめ 7. 質疑応答
    • Template Meta Programming応用 1. 関連知識をおさえる 2. 実践(有用なイディオムと例)
    • Template Meta Programming応用 これからTMPの応用に入る その前に関連知識を知っておく
    • Template Meta Programming応用 ・constexpr ・decltype ・SFINAE
    • Template Meta Programming応用 補足 constexpr
    • Template Meta Programming応用 C++11のconstexprに関して軽く説明する 私は陶芸家ではないので今回は深入りはしない constexprに関して深く知りたい場合は ・中3女子でもわかる!constexpr ・中3女子が教える本当に気持ちのいい  constexpr ・constexpr中3女子テクニック などの有益な情報の載ったスライドがWeb上に公開されて いるので各自Web上で検索して読むなどすると良い
    • Template Meta Programming応用 TMPにおける値の計算は整数しか扱えないと いう弱点がある (浮動小数点を無理矢理エミュレートしようとする人は居るが…) (また、一応ratioのようなものもあるが…) またコンパイル時にしか使えない int a = 0; add<a, 1>::value; // エラー、aは実行時オブジェクト
    • Template Meta Programming応用 C++03やconstexpr非対応のコンパイラの場 合はTMPを使うが、そうでない場合はただ 値を計算して、値を返すだけのようなメタ関 数はconstexpr関数で実装した方が好ましい 事も多いと思われる
    • Template Meta Programming応用 // コンパイル時に計算可能 template<class T> constexpr T add(T x, T y) { return x + y; } 見慣れた関数の形で実装出来る 実行時にも共通のインタフェースで使える 浮動小数点もOK
    • Template Meta Programming応用 次のような式はconstant expressionsと呼ばれる。(Constant expressionはコンパイル時に評価され得る) constant-expression: conditional-expression 以降の条件部分はかなり細かいので、発表では適当に流す ので各自確認しておいて欲しい 次ページ以降の条件に当てはまる場合、constexprにはなれ ない
    • Template Meta Programming応用 条件 ・this (非静的メンバ関数の本体に暗黙の変換の結果を含む、クラスメンバアク セス式の後置式、として表れていない場合) ・リテラルクラスのconstexprコンストラクタとconstexpr関数以外の関数呼び出 し(オーバーロードの解決は普段通り適用される) ・constexpr関数やconstexprコンストラクタの定義の外での未定義動作の constexpr関数や、未定義動作のconstexprコンストラクタの呼び出し
    • Template Meta Programming応用 条件 ・constant expressionを生成しないような引数を用いてのconstexpr関数の呼び出 し 例 constexpr const int* addr(const int& ir) { return &ir; } // OK static const int x = 5; constexpr const int* xp = addr(x); // OK constant expressionなアドレス constexpr const int* tp = addr(5); // エラー 一時アドレスの取得はconstant expressionではない
    • Template Meta Programming応用 条件 ・constant expressionを生成しないような引数による、初期化リストのみからな るconstexprコンストラクタの呼び出し 例 int x; // not constant struct A { constexpr A(bool b) : m(b?42:x) { } int m; }; constexpr int v = A(true).m; // OK constexpr int w = A(false).m; // エラー mの初期化に用いられるxが定数でない
    • Template Meta Programming応用 条件 ・constexpr関数又はconstexprコンストラクタの再帰呼び出しが処理系の定義す る制限を超える場合(Annex Bを参照) Annex BにおいてRecursive constexpr function invocations [512].と定義されている ・数学上定義されないような値、又は特定の型で扱える範囲外のその型の値。 ・ラムダ式 ・次の場合を除く左辺値から右辺値への変換  ・constant expressionで事前に初期化された非volatileなconstオブジェクトを参 照する整数型のglvalue又はenum  ・constexpr修飾で定義された、非volatileのオブジェクトかそのようなサブオ ブジェクトを参照するリテラル型のglvalue  ・constant expressionで初期化された寿命の尽きていない一時オブジェクトを 参照するリテラル型のglvalue
    • Template Meta Programming応用 条件 ・非activeなunionのメンバやサブオブジェクトを参照するglvalueに適用される左 辺値から右辺値への変換 ・constant expressionによって初期化されていない状態の値やデータメンバ参照 を参照するid-expression ・dynamic cast ・reinterpret_cast ・擬似destructorの呼び出し ・インクリメント及びデクリメント操作 ・多態クラス型に対するtypeid式 ・new式 ・delete式
    • Template Meta Programming応用 条件 ・ポインタ同士の減算 ・結果が不特定な場所での比較に関連するような演算子(rational or equality operator) ・代入及び複合代入 ・throw式
    • Template Meta Programming応用 更に補足 実行時計算とコンパイル時計算の浮動小数点 演算の精度は異なる場合がある
    • Template Meta Programming応用 規格では浮動小数点の演算精度に関して制限を課さないの で、コンパイル時と実行時における同じ式の浮動小数点演 算の結果が同じである保証は無い (N3337 5.19 Constant expressions) bool f() { char array[1 + int(1 + 0.2 - 0.1 - 0.1)]; // 必ずコンパイル時に計算される int size = 1 + int(1 + 0.2 - 0.1 - 0.1); // 実行時に計算されるかもしれない return sizeof(array) == size; } f()の結果がtrueであるかfalseであるかは未定義である。
    • Template Meta Programming応用 ・整数演算しか出来ないTMPに比べ  constexprは浮動小数点演算もコンパイル  時に出来る ・コンパイル時計算にしか使えないTMPに比  べconstexprは実行時計算にも使える ・逆に型に関する処理はTMPでしか実現出  来ない(TMPの独壇場)
    • Template Meta Programming応用 補足 decltype
    • Template Meta Programming応用 decltype イメージとしてはsizeofの型バージョン
    • Template Meta Programming応用 decltype The type denoted by decltype(e) is defined as follows: — if e is an unparenthesized id-expression or an unparenthesized class member access (5.2.5), decltype(e) is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed; — otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e; — otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e; — otherwise, decltype(e) is the type of e.
    • Template Meta Programming応用 decltype if e is an unparenthesized id-expression or an unparenthesized class member access ... 要するにexpressionが括弧で囲まれていないような decltype(expression)の形のものの場合
    • Template Meta Programming応用 例 char a; decltype(a); // char int b = 1; int& c = b; decltype(c); // int& const int* d; decltype(d); // const int*
    • Template Meta Programming応用 例 struct a_type { using type = int; }; a_type a; decltype(a); // a_type decltype(a)::type; // int void f(int); decltype(f); // void (int)
    • Template Meta Programming応用 例 void f(int); void f(double); decltype(f); // Error 関数に複数のオーバーロードがあるとダメ
    • Template Meta Programming応用 例 int f(); decltype(f()); // int 関数の戻り値の型も取れる decltype(e)のeのexpressionが評価されることはないので関数 の定義は必要ない(TMPにおいて重要)
    • Template Meta Programming応用 decltype decltype((expression))の形のものの場合
    • Template Meta Programming応用 decltype otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e; decltype((expression))のような括弧で囲まれ たもので、かつxvalueの場合T&&になる xvalueはrvalue referenceを返す関数の呼び出しとそれに準ず るもののイメージ(std::moveなども含まれる)
    • Template Meta Programming応用 decltype otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e; decltype((expression))のような括弧で囲まれ たもので、かつlvalueの場合T&になる
    • Template Meta Programming応用 decltype otherwise, decltype(e) is the type of e. それ以外(prvalue等)はeの型 例 decltype(1); // int decltype("hello,world"); // const char[12]
    • Template Meta Programming応用 decltypeまとめ ・decltypeを使うとオブジェクトや関数の戻  り値の型を得る事が出来る ・decltypeの結果は括弧の有無で変わる
    • Template Meta Programming応用 補足 SFINAE
    • Template Meta Programming応用 SFINAE Substitution failure is not an error
    • Template Meta Programming応用 テンプレートの置き換えに失敗してもすぐ にはエラーにならず他の候補を探す 必修知識だがC++入門書には多分載ってない (結果として知らない人も居る印象)
    • Template Meta Programming応用 言葉より実例を見るほうが早い
    • Template Meta Programming応用 struct Test { typedef int foo; }; template <typename T> void f(typename T::foo) {} // Definition #1  template <typename T> void f(T) {} // Definition #2  int main() { f<Test>(10);  f<int>(10);  }
    • Template Meta Programming応用 struct Test { typedef int foo; }; template <typename T> void f(typename T::foo) {} // Definition #1  template <typename T> void f(T) {} // Definition #2  int main() { f<Test>(10);  f<int>(10);  }
    • Template Meta Programming応用 struct Test { typedef int foo; }; template <typename T> void f(typename T::foo) {} // Definition #1  template <typename T> void f(T) {} // Definition #2  int main() { f<Test>(10);  f<int>(10);  }
    • Template Meta Programming応用 struct Test { typedef int foo; }; template <typename T> void f(typename T::foo) {} // Definition #1 定義にマッチする template <typename T> void f(T) {} // Definition #2  int main() { f<Test>(10); // Call #1.  f<int>(10);  }
    • Template Meta Programming応用 struct Test { typedef int foo; }; template <typename T> void f(typename T::foo) {} // Definition #1 int::fooは存在しない template <typename T> void f(T) {} // Definition #2  int main() { f<Test>(10); // Call #1.  f<int>(10);  }
    • Template Meta Programming応用 struct Test { typedef int foo; }; template <typename T> void f(typename T::foo) {} // Definition #1 呼出候補から外れる template <typename T> void f(T) {} // Definition #2  int main() { f<Test>(10); // Call #1.  f<int>(10);  }
    • Template Meta Programming応用 struct Test { typedef int foo; }; template <typename T> void f(typename T::foo) {} // Definition #1 エラーにはならない template <typename T> void f(T) {} // Definition #2 定義にマッチする int main() { f<Test>(10); // Call #1.  f<int>(10); // Call #2.  }
    • Template Meta Programming応用 このようなルールをSFINAEと呼ぶ 条件分岐、エラーチェック等を実現出来る (例は後述)
    • Template Meta Programming応用 1. 関連知識をおさえる 2. 実践(有用なイディオムと例)
    • Template Meta Programming応用 関連知識をおさえたところでいよいよ応用 として実用的なイディオム等を見ていく
    • Template Meta Programming応用 ・Variadic templates   可変長引数テンプレートを扱う ・enable_if   SFINAEによる条件分岐 ・index_tuple, index_range (index_sequence)   整数列パラメータパックの生成と利用 ・Expression Template(ET)  式テンプレート
    • Template Meta Programming応用 Variadic templates  可変長引数テンプレートを扱う
    • Template Meta Programming応用 要素の追加 型リストを保持する単純なクラス template<class... Types> struct list { template<class Type> struct push_back { using type = list<Types..., Type>; // これでOK }; };
    • Template Meta Programming応用 要素の追加 例えばlist<int, char>::push_back<double>::typeのとき Types...はint, charに展開され、Typeはdoubleであるから list<Types..., Type>はlist<int, char, double>
    • Template Meta Programming応用 要素の追加 template<class... Types> struct list { template<class Type> struct push_front { using type = list<Type,Types...>; // これでOK }; }; push_frontも同様に可能
    • Template Meta Programming応用 要素の削除 template<class... Types> struct list { private: template<class Type, class... Types2> struct pop_front_impl { using type = list<Types2...>; }; public: struct pop_front { using type = typename pop_front_impl<Types...>::type; }; };
    • Template Meta Programming応用 要素の削除 template<class Type, class... Types2> struct pop_front_impl { using type = list<Types2...>; }; 複数受け取ったパラメータのうちの最初の1つだけTypeに入 り、残りは全てTypes2に入る
    • Template Meta Programming応用 要素の削除 pop_backだけは簡単にはいかない 実装も他の操作に比べ面倒、計算コストもかかる template<class... Types2, class Type> // エラー、可変長引数テン プレートはテンプレートパラメータのうち最後でなければ ならない 再帰的な実装など工夫が必要
    • Template Meta Programming応用 enable_if  SFINAEによる条件分岐
    • Template Meta Programming応用 enable_if ・SFINAEを利用した条件分岐を手助けする  イディオム ・標準ライブラリにstd::enable_ifがある
    • Template Meta Programming応用 enable_if template<bool B, class T = void> struct enable_if {}; template<class T> struct enable_if<true, T> { typedef T type; }; つまり条件を満たした場合のみ std::enable_if<条件>::type が定義される(SFINAEに利用出来る)
    • Template Meta Programming応用 例 template<class T> T foo2 ( T t, typename std::enable_if<std::is_integral<T>::value>::type* = 0) { return t; } Tが整数型だった場合のみ呼び出し可能な関数 Tが非整数型なら呼出候補から外される (もし呼出候補が1つも見つからなければコンパイルエラー) 不正な型の引数が渡される事を防止出来る
    • Template Meta Programming応用 例 template<class T, class Enable = void> class A; // undefined template<class T> class A<T, typename std::enable_if<std::is_floating_point<T>::value>::type> { }; 条件を満たした場合のみ構築可能なクラスを部分特殊化で 実現する SFINAEの対象は関数に限られたものではない
    • Template Meta Programming応用 もっとenable_if  enabler idiom
    • Template Meta Programming応用 従来のenable_ifではオーバーロード出来ない template<typename T, typename Enable = typename std::enable_if <std::is_integral<T>::value>::type> void f(T); template<typename T, typename Enable = typename std::enable_if<std::is_pointer<T>::value>::type> void f(T); // デフォルト引数が異なるだけの再定義はダメ
    • Template Meta Programming応用 type-parameterでのオーバーロードは厳しい
    • Template Meta Programming応用 そこで以下のような宣言を行う extern void * enabler;
    • Template Meta Programming応用 extern void * enabler; の定義は必要ない enablerはextern修飾されているので外部リン ケージを持つ ということは
    • non-type template-parameter non-type template-parameterに渡せるもの 外部リンケージのあるオブジェクト ・extern修飾されたもの
    • Template Meta Programming応用 これはオーバーロード出来る template<typename T, typename std::enable_if< std::is_integral<T>::value>::type *& = enabler> void f(T); template<typename T, typename std::enable_if< std::is_pointer<T>::value>::type *& = enabler> void f(T);
    • Template Meta Programming応用 template<typename T, typename std::enable_if< std::is_integral<T>::value>::type *& = enabler> void f(T); enable_ifを非型パラメータとして用いているのがポイント (参照型は非型パラメータになれる) enablerは外部リンケージを持つので非型パラメータに渡す 事が出来る
    • Template Meta Programming応用 わざわざ使わなくても十分なケースが多い しかし、必要になることもある
    • Template Meta Programming応用 index_tuple, index_range (integer_sequence)  整数列パラメータパックの生成と利用
    • Template Meta Programming応用 index_tuple idiomなどと呼ばれる ・整数列のパラメータパックを生成するイ  ディオム ・C++14からはstd::integer_sequenceとして  同様の機能が標準ライブラリ入り ・インデックスアクセス可能なデータ構造  の効率の良い要素の走査、リスト内包表  記相当の処理の実現
    • Template Meta Programming応用 ・整数列パラメータを保持する型 ・整数列パラメータを生成するヘルパメタ  関数 のセットで用いられる
    • Template Meta Programming応用 例えばNを正の整数としたとき index_range<0, N>::typeが index_tuple<0,1,2,3, ..., N-1>  と同じ型になるように実装する
    • Template Meta Programming応用 実装例(簡単でクソなO(N)オーダーでの実装) template<std::size_t... Indices> struct index_tuple {}; template<std::size_t step, std::size_t last, class result = index_tuple<>, bool flag = step >= last> struct index_range { using type = result; }; template<std::size_t step, std::size_t last, std::size_t... Indices> struct index_range<step, last, index_tuple<Indices...>, false> : index_range<step + 1, last, index_tuple<Indices..., step>> {};
    • Template Meta Programming応用 実装例(簡単でクソなO(N)オーダーでの実装) template<std::size_t... Indices> struct index_tuple {}; template<std::size_t step, std::size_t last, class result = index_tuple<>, bool flag = step >= last> struct index_range { using type = result; }; template<std::size_t step, std::size_t last, std::size_t... Indices> struct index_range<step, last, index_tuple<Indices...>, false> : index_range<step + 1, last, index_tuple<Indices..., step>> {};
    • Template Meta Programming応用 template<std::size_t... Indices> struct index_tuple {}; 整数列パラメータを保持する型 例 index_tuple<0,1,2,3,4,5> // 0,1,2,3,4,5を保持
    • Template Meta Programming応用 index_rangeは整数列パラメータを生成する ヘルパメタ関数 ・結果を返す定義(デフォルト定義) ・整数列パラメータを生成するフローの為  の部分特殊化 の2つの定義がある
    • Template Meta Programming応用 実装例(簡単でクソなO(N)オーダーでの実装) template<std::size_t... Indices> struct index_tuple {}; template<std::size_t step, std::size_t last, class result = index_tuple<>, bool flag = step >= last> struct index_range { using type = result; }; template<std::size_t step, std::size_t last, std::size_t... Indices> struct index_range<step, last, index_tuple<Indices...>, false> : index_range<step + 1, last, index_tuple<Indices..., step>> {};
    • Template Meta Programming応用 結果を返すデフォルト定義 template< std::size_t step, std::size_t last, class result = index_tuple<>, bool flag = first >= last > struct index_range { using type = result; };
    • Template Meta Programming応用 パラメータの解説 template<  std::size_t step, // 現在処理している数(呼出は初期値を渡す) std::size_t last, // 範囲の終わり(さきほどのN) class result = index_tuple<>, // 結果型(デフォでindex_tuple<>) bool flag = first >= last // 再帰の終了条件 > struct index_range { using type = result; // 型を定義して結果を返す };
    • Template Meta Programming応用 実装例(簡単でクソなO(N)オーダーでの実装) template<std::size_t... Indices> struct index_tuple {}; template<std::size_t step, std::size_t last, class result = index_tuple<>, bool flag = step >= last> struct index_range { using type = result; }; template<std::size_t step, std::size_t last, std::size_t... Indices> struct index_range<step, last, index_tuple<Indices...>, false> : index_range<step + 1, last, index_tuple<Indices..., step>> {};
    • Template Meta Programming応用 生成フローの定義 template< std::size_t step, // 現在処理している数 std::size_t last, // 範囲の終わり std::size_t... Indices // 結果の整数列パラメータ > struct index_range< step, last, index_tuple<Indices...>, false>  : index_range<step + 1, last, index_tuple<Indices..., step>> {};
    • Template Meta Programming応用 生成フローの定義 template< std::size_t step, // 現在処理している数 std::size_t last, // 範囲の終わり std::size_t... Indices // 結果の整数列パラメータ > struct index_range< step, last, index_tuple<Indices...>, false> // falseで特殊化 : index_range<step + 1, last, index_tuple<Indices..., step>> {};
    • Template Meta Programming応用 デフォルト定義における以下の部分 bool flag = first >= last // 再帰の終了条件 がfalseになったときだけ部分特殊化された定義が使われる すなわち生成フローの定義が使われる
    • Template Meta Programming応用 生成フローの定義 template< std::size_t step, // 現在処理している数 std::size_t last, // 範囲の終わり std::size_t... Indices // 結果の整数列パラメータ > struct index_range< step, last, index_tuple<Indices...>,  false> // falseで特殊化 : index_range<step + 1, last, index_tuple<Indices..., step>> {};
    • Template Meta Programming応用 生成フローの定義 template< std::size_t step, // 現在処理している数 std::size_t last, // 範囲の終わり std::size_t... Indices // 結果の整数列パラメータ > struct index_range< step, last, index_tuple<Indices...>, // 結果型はindex_tupleにIndicesを渡す false> // falseで特殊化 : index_range<step + 1, last, index_tuple<Indices..., step>> {};
    • Template Meta Programming応用 template< std::size_t step, // 現在処理している数 std::size_t last, // 範囲の終わり std::size_t... Indices // 結果の整数列パラメータ > struct index_range< step, last, index_tuple<Indices...>, // 結果型はindex_tupleにIndices渡す false> // falseで特殊化 : index_range<step + 1, last, index_tuple<Indices..., step>> // パラメータ違いのindex_rangeを継承している {};
    • Template Meta Programming応用 テンプレートパラメータ違いの自分自身を 継承すること : index_range<step + 1, last, index_tuple<Indices..., step>>  メタ関数における再帰を意味する テンプレートパラメータは関数の引数相当
    • Template Meta Programming応用 : index_range<step + 1, last, index_tuple<Indices..., step>>  この部分ではstepを1進め 可変長引数テンプレートパラメータの要素 にstepを追加して再帰する index_tupleに渡す引数を増やしながら再帰している
    • Template Meta Programming応用 結果を返すデフォルト定義に戻る template< std::size_t step, std::size_t last, class result = index_tuple<>, // (index_tuple<Indices..., step>) bool flag = first >= last // stepがlastに達した時点で再帰が止まる > struct index_range { using type = result; // 結果型 };
    • Template Meta Programming応用 実用例
    • Template Meta Programming応用 配列クラスと値nを受けとり、全要素にnを加えた値の要素を持つ配列クラスを 返す関数iota template<class T, std::size_t N, std::size_t... Indices> constexpr auto iota_impl(const array<T, N> arr, const T n, index_tuple<Indices...>) -> array<T, N> { return array<T, N>{{(arr[Indices] + n)...}}; } template<class T, std::size_t N> constexpr auto iota(const array<T, N> arr, const T n) -> array<T, N> { return iota_impl(arr, n, typename index_range<0, N>::type()); } constexpr array<int, 9> arr1{{1,2,3,4,5,6,7,8,9}}; // 配列クラスのオブジェクト constexpr auto arr2 = iota(arr1, 1);  // iotaには配列クラスのオブジェクトと値nを渡してやる //結果としてarray<int, 9>{{2,3,4,5,6,7,8,9,10}}が欲しい
    • Template Meta Programming応用 template<class T, std::size_t N, std::size_t... Indices> constexpr auto iota_impl(const array<T, N> arr, const T n, index_tuple<Indices...>) -> array<T, N> { return array<T, N>{{(arr[Indices] + n)...}}; } template<class T, std::size_t N> constexpr auto iota(const array<T, N> arr, const T n) -> array<T, N> { return iota_impl(arr, n, typename index_range<0, N>::type()); } constexpr array<int, 9> arr1{{1,2,3,4,5,6,7,8,9}}; constexpr auto arr2 = iota(arr1, 1); // array<int, 9>{{2,3,4,5,6,7,8,9,10}} Tはintに、Nは9に推論される
    • Template Meta Programming応用 template<class T, std::size_t N, std::size_t... Indices> constexpr auto iota_impl(const array<T, N> arr, const T n, index_tuple<Indices...>) -> array<T, N> { return array<T, N>{{(arr[Indices] + n)...}}; } template<class T, std::size_t N> constexpr auto iota(const array<T, N> arr, const T n) -> array<T, N> { return iota_impl(arr, n, typename index_range<0, N>::type()); } constexpr array<int, 9> arr1{{1,2,3,4,5,6,7,8,9}}; constexpr auto arr2 = iota(arr1, 1); // array<int, 9>{{2,3,4,5,6,7,8,9,10}} この場合N = 9であるからindex_range<0, 9>::typeになる
    • Template Meta Programming応用 template<class T, std::size_t N, std::size_t... Indices> constexpr auto iota_impl(const array<T, N> arr, const T n, index_tuple<Indices...>) -> array<T, N> { return array<T, N>{{(arr[Indices] + n)...}}; } template<class T, std::size_t N> constexpr auto iota(const array<T, N> arr, const T n) -> array<T, N> { return iota_impl(arr, n, typename index_range<0, N>::type()); } constexpr array<int, 9> arr1{{1,2,3,4,5,6,7,8,9}}; constexpr auto arr2 = iota(arr1, 1); // array<int, 9>{{2,3,4,5,6,7,8,9,10}} index_range<0, 9>::typeはindex_tuple<0,1,2,3,4,5,6,7,8>である
    • Template Meta Programming応用 template<class T, std::size_t N, std::size_t... Indices> constexpr auto iota_impl(const array<T, N> arr, const T n, index_tuple<Indices...>) -> array<T, N> { return array<T, N>{{(arr[Indices] + n)...}}; } template<class T, std::size_t N> constexpr auto iota(const array<T, N> arr, const T n) -> array<T, N> { return iota_impl(arr, n, typename index_range<0, N>::type()); } constexpr array<int, 9> arr1{{1,2,3,4,5,6,7,8,9}}; constexpr auto arr2 = iota(arr1, 1); // array<int, 9>{{2,3,4,5,6,7,8,9,10}} 結果としてindex_tuple<0,1,2,3,4,5,6,7,8>()がiota_implの引数 として渡される
    • Template Meta Programming応用 template<class T, std::size_t N, std::size_t... Indices> // Indices is 0,1,2,3,4,5,6,7,8 constexpr auto iota_impl(const array<T, N> arr, const T n, index_tuple<Indices...>) -> array<T, N> { return array<T, N>{{(arr[Indices] + n)...}}; } template<class T, std::size_t N> constexpr auto iota(const array<T, N> arr, const T n) -> array<T, N> { return iota_impl(arr, n, typename index_range<0, N>::type()); } constexpr array<int, 9> arr1{{1,2,3,4,5,6,7,8,9}}; constexpr auto arr2 = iota(arr1, 1); // array<int, 9>{{2,3,4,5,6,7,8,9,10}} このときIndicesは0,1,2,3,4,5,6,7,8に推論される
    • Template Meta Programming応用 template<class T, std::size_t N, std::size_t... Indices> // Indices is 0,1,2,3,4,5,6,7,8 constexpr auto iota_impl(const array<T, N> arr, const T n, index_tuple<Indices...>) -> array<T, N> { return array<T, N>{{(arr[Indices] + n)...}}; } template<class T, std::size_t N> constexpr auto iota(const array<T, N> arr, const T n) -> array<T, N> { return iota_impl(arr, n, typename index_range<0, N>::type()); } constexpr array<int, 9> arr1{{1,2,3,4,5,6,7,8,9}}; constexpr auto arr2 = iota(arr1, 1); // array<int, 9>{{2,3,4,5,6,7,8,9,10}} 最終的にarray<T, N>{{(arr[Indices] + n)...}}の部分は array<int, 9>{{arr[0] + n, arr[1] + n, (中略)..., arr[8] + n}} という風に展開される
    • Template Meta Programming応用 基本的にインデックスアクセス可能なデー タ構造であれば適用出来る
    • Template Meta Programming応用 余談 生成結果のメモ化
    • Template Meta Programming応用 template<std::size_t First, std::size_t Last> struct index_tuple_memo { static constexpr auto value = typename index_range<First, Last>::type(); }; index_tuple系の生成結果のメモ化を行う試み typename index_range::type()の呼び出し時に index_tuple_memo::valueを代わりに使う事で使いまわす 2回目以降の呼び出しでO(1)が期待出来る
    • Template Meta Programming応用 ・メモリ使用量が増える可能性 ・コンパイラの最適化によっては無意味 ・逆効果かも 同じ要素数で尚且つ数千要素のtuple展開を何度も行うよう な場合には有効かもしれないが通常は使わなくて良さそう
    • Template Meta Programming応用 index_rangeの実装には簡単でクソなO(N)オーダーでの実装 の他に有用なO(log2(N))オーダーでの実装法も知られている
    • Template Meta Programming応用 線形オーダーでは扱える要素数が限られ、生成速度も遅い のでSproutや標準ライブラリで後者の実装が使われている
    • Template Meta Programming応用 アイデア概要 例えばN = 14のとき Indicesが0,1,2,3,4,5,6であれば index_tuple<Indices..., (Indices + N/2)...>  を展開すれば index_tuple<0,1,2,3,4,5,6,7,8,9,10,11,12,13>  が得られる 現実には実装はもっと面倒だが基本的には、このように前 の結果を利用して要素数を倍々にしていくという考え
    • Template Meta Programming応用 詳しい実装に関してはsprout::index_rangeなどを見て欲しい
    • Template Meta Programming応用 Expression Template(ET) 式テンプレート
    • Template Meta Programming応用 ・式の評価を遅延させる ・余計な一時オブジェクトの生成とコピーの  発生を防ぐ
    • Template Meta Programming応用 通常、巨大な行列の一時オブジェクトが生 成される演算に対して、演算の結果ではな く演算を表す型オブジェクトを返す
    • Template Meta Programming応用 行列のA, B, Cの和 A+B+C ETを使わない実装では、A + Bで値を計算し て一時オブジェクトを生成、更に一時オブ ジェクト + Cで値を計算して一時オブジェク トを生成という事が起こりうる その過程で巨大なオブジェクトのコピー、構 築が起こりうる
    • Template Meta Programming応用 行列のA, B, Cの和 Plus<Plus<Vector<A>,Vector<B>>,Vector<C>> 演算を型として表す、巨大な行列をコピーし たりしなくて済むので、途中式の段階で一 時オブジェクトの生成コストが非常に小さ くなる
    • Template Meta Programming応用 値はoperator=()が呼ばれた時に初めて評価さ れる
    • Template Meta Programming応用 参考 ETの簡単な実装法やデメリット等も含めて 紹介されている 日本で一番分かりやすく書いたつもりの Expression Templateの説明 http://d.hatena.ne.jp/Cryolite/20040506 などが分かりやすい
    • おしながき 1. Templateの基本確認 2. Template Meta Programmingとは 3. Template Meta Programming入門 4. Template Meta Programming応用 5. その他付録 6. まとめ 7. 質疑応答
    • その他付録 1. TMPとは直接関係ないイディオムなど 2. これからのTMPと関連イディオムなど
    • その他付録 型を受け取るユーザ定義リテラル
    • その他付録 整数リテラルと浮動小数点リテラルに限り char...のテンプレート実引数として受け取る ことが出来る
    • その他付録 template<char...> void operator "" _to_string() {}; 12345_to_string; // template<‘1’,’2’,’3’,’4’,’5’>として渡される 3.14_to_string; // template<‘3’,’.’,’1’,,4>
    • その他付録 #include <sprout/index_tuple.hpp> template<char last> constexpr auto indices() -> typename sprout::index_range<0, last - '0'>::type { return sprout::index_range<0, last - '0'>::make(); } template<char... last> constexpr auto operator "" _indices() -> decltype(indices<last...>()) { static_assert(sizeof...(last) == 1, ""); return indices<last...>(); } int main() { static_assert(std::is_same<decltype(2_indices), sprout::index_tuple<0,1>>::value, ""); } ユーザ定義リテラルを使ったindex_tupleの生成みたいなサンプル (実際にはchar同士の減算等にはもう少し気を配った方がいい感じがしますが)
    • その他付録 型文字列の生成に使える ただし整数と浮動小数点のみ ショージキ微妙だが今後に期待
    • その他付録 private namespace idiom  detail等へのアクセスを制限する
    • その他付録 namespace tmp {  namespace { // 無名名前空間 namespace detail { // detailへはアクセスされたくない }  void f() { detail::f_impl(); } // publicなインタフェース }  namespace detail {}; // detailのメンバへのアクセスを殺す void f_impl() {} // privateな実装 }  tmp::f(); // OK  tmp::detail::f_impl(); // ルックアップに失敗しエラー
    • その他付録 無名名前空間が暗黙にusingされる事を逆手に取る detailへのアクセスを殺せるので名前空間スコープで、メタ 関数の内部実装にどうしてもアクセスされたくない場合に は有効
    • その他付録 1. TMPとは直接関係ないイディオムなど 2. これからのTMPと関連イディオムなど
    • その他付録 先程取り上げたinteger_sequenceの他にもあ る、C++14(所謂N3690辺り) / C++1y以降に 入ると思われる機能とそれに関連する話 (大雑把に未来の話です) ただしTMPに割と関連しそうなものだけ
    • その他付録 C++14以降で(ほぼ確実に)入る機能
    • その他付録 constexprの制限緩和
    • その他付録 ・副作用、制御構文が使えるようになる ・ポインタ同士の減算が定数式になる など 一部制限が増えている ・a conversion from type cv void * to a pointer-to-object type; C++11ではreinterpret_castが禁止されていたのにvoid*の型 変換が禁止されていなかったが、これもダメに修正された
    • その他付録 ただし、constexpr関数では相変わらずエラーハンドリング が困難なので 闊に副作用の濫用を行うと、例外安全性を 保つのが難しいので注意が必要 その他、細かい事は規格等を参照して欲しい
    • その他付録 decltype(auto)
    • その他付録 decltype(auto)は、式をdecltype()の中に書い たのと同じ挙動になる
    • その他付録 江添さんの記事が分かりやすい http://cpplover.blogspot.jp/2013/08/decltypeauto.html
    • その他付録 記事の例のみ引用する int i = 0 ; int && f() ; auto a1 = i ; // int decltype(auto) a2 = i ; // int auto b1 = (i) ; // int decltype(auto) b2 = (i) ; // int & auto c1 = f() ; // int decltype(auto) c2 = f() ; // int &&
    • その他付録 関数の戻り値などに利用出来る template < typename T, typename U > auto g( T const & t, U const & u ) -> decltype( auto ) // decltype( f( t, u ) )と書かなくて済む { return f( t, u ) ; }
    • その他付録 ジェネリックラムダ
    • その他付録 ラムダ式もテンプレート化出来る auto NumElements = []<int N>(auto (&a)[N]) { return N; };
    • その他付録 Variable Templates  変数テンプレート
    • その他付録 読んで字の如く、変数をテンプレート化する
    • その他付録 変数テンプレート template<class T> constexpr T pi = static_cast<T>(3.14); pi<int> == 3; pi<float> == 3.14; 異なる精度の値を得る
    • その他付録 変数テンプレート template<class T1, T2> constexpr bool is_same_v = std::is_same<T1, T2>::value; is_same_v<int, double>; // false 面倒な::valueを書かずに済む
    • その他付録 C++1y(ここではC++14よりも後を意味する) 以降で入るかもしれない提案
    • その他付録 Concepts Lite  軽量コンセプト
    • その他付録 C++11入りする予定だったConceptの軽量・改良版 幾つかペーパーのようなものが出ている模様
    • その他付録 プログラマにやさしい構文で型制約を行えるようになる constexpr関数として型制約を定義する案など 使えるようになるのは当分先の話になりそうな気はします
    • その他付録 N3741 Opaque Alias
    • その他付録 Strong typedef  ある型から別の型を作る using identifier = access-specifier type-id opaque-definition
    • その他付録 using age = private int; // intとは別の型として認識される // privateにより暗黙のキャストは許可しない age age1 = static_cast<age>(20); // 明示的なキャストをする age age2 = 18; // Error ageはintではない これまでのtypedefやusingではこのような事は出来ない
    • その他付録 オーバーロード等も有効になる int f(int); age f(age); 別物として扱われる
    • その他付録 N3730 Specializations and namespaces
    • その他付録 ネストされた名前空間を閉じなくても特殊化を書けるよう にする提案
    • その他付録 namespace A { namespace B { /* ... */ class C { /* ... */ }; } } // 一度名前空間を抜ける namespace std { template<> // 特殊化を書く struct hash<A::B::C> { size_t operator()(A::B::C const &c) { /* ... */ } }; } namespace A { /* Reenter namespace I am using */ namespace B { /* ... */ } }
    • その他付録 namespace A { namespace B { /* ... */ class C { /* ... */ }; template<> struct ::std::hash<C> { std::size_t operator()(C const &c) { /* ... */ } }; /* ... */ } } このように書けるようになる
    • その他付録 N3728 Packaging Parameter Packs
    • その他付録 パラメータパックを保持するための型リストをサポートす る提案 パラメータパックが間違いなく扱いやすくなる 具体例はN3728ペーパー参照
    • その他付録 N3413 Allowing arbitrary literal types for non-type template parameters
    • その他付録 非型パラメータとして任意のリテラル型を許容しようとい う提案
    • その他付録 struct C { constexpr C(int v) : v(v) { } int v; }; template<C c> // !? struct X { int array[c.v]; }; int main() { X<C(42)> x; }  C++の世界の法則が乱れる完全にヤバい提案
    • その他付録 N3761 Proposing type_at<>
    • その他付録 パラメータパックのN番目の要素にアクセス する機能のメタ関数を標準に入れる提案
    • その他付録 簡単な実装案が書かれている 要するにN番目の要素に到達するまで線形再帰する template <unsigned N, typename T, typename ...R> struct type_at { using type = typename type_at<N - 1, R...>::type; }; template <typename T, typename ...R> struct type_at<0, T, R...> { using type = T; };
    • その他付録 簡単な実装案が書かれている 要するにN番目の要素に到達するまで線形再帰する template <unsigned N, typename T, typename ...R> auto value_at(T&&, R&&... r) -> decltype(auto) { return value_at<N - 1, R...>(std::forward<R>(r)...); } template auto value_at(T&& t, R&&...) -> decltype(auto) { return std::forward<T>(t); }
    • その他付録 対数オーダーで実装可能なのでAuthorに実装 の改良を提案する意見を送付した
    • その他付録 対数オーダーでの要素アクセスの実装
    • その他付録 次のようなメタ関数を用意する template<std::size_t N, class T = void> using make_type_sequence = typename make_type_sequence_impl<N, T>::type; // make N-1 Ts type_sequence<T, T, ..., T> O(log2(N)) implでindex_rangeのように型列を対数オーダーで生成する
    • その他付録 次のようなメタ関数を用意する template<class T> struct type_wrapper { using type = T; }; ただ型を保持するだけの型
    • その他付録 template<class T, std::size_t N> struct type_at { private: template<template<class...> class Type, class... Args> static auto impl(type_wrapper<Type<Args...>>) -> typename type_at_impl<N, type_wrapper<Args>...>::type; public: using type = typename decltype(impl(type_wrapper<T>()))::type; };  型リストを持つコンテナ型TとインデックスNを受け取る
    • その他付録 template<class T, std::size_t N> struct type_at { private: template<template<class...> class Type, class... Args> static auto impl(type_wrapper<Type<Args...>>) -> typename type_at_impl<N, type_wrapper<Args>...>::type; public: using type = typename decltype(impl(type_wrapper<T>()))::type; };  type_wrapperに包んでTをimplに渡す
    • その他付録 type_wrapperの役割 ・インスタンス化出来ない型も処理出来るようにする
    • その他付録 type_wrapperの役割 using type = typename decltype(impl(type_wrapper<T>()))::type; 例えばTが template<class...>  struct list;  のようなインスタンス化不可能な型の場合 impl(list())はインスタンス化出来ずエラーになるが impl(type_wrapper<T>())ならばインスタンス化出来て渡せる
    • その他付録 type_wrapperの役割 ・トップレベルのcv修飾を保持する
    • その他付録 type_wrapperの役割 impl(type_wrapper<T>())ならば値渡しの際もArgument Deductionにおいて、Tのcv修飾情報が失われずに済む
    • その他付録 template<class T, std::size_t N> struct type_at { private: template<template<class...> class Type, class... Args> static auto impl(type_wrapper<Type<Args...>>) -> typename type_at_impl<N, type_wrapper<Args>...>::type; public: using type = typename decltype(impl(type_wrapper<T>()))::type; };  template template parameterを使えばテンプレートクラスの 持つ型リストを抽出する事が出来る
    • その他付録 template<class T, std::size_t N> struct type_at { private: template<template<class...> class Type, class... Args> static auto impl(type_wrapper<Type<Args...>>) -> typename type_at_impl<N, type_wrapper<Args>...>::type; public: using type = typename decltype(impl(type_wrapper<T>()))::type; };  auto f() -> の形式の戻り値指定を使えば戻り値型部分でも parameter packを展開したり出来る
    • その他付録 template<class T, std::size_t N> struct type_at { private: template<template<class...> class Type, class... Args> static auto impl(type_wrapper<Type<Args...>>) -> typename type_at_impl<N, type_wrapper<Args>...>::type; public: using type = typename decltype(impl(type_wrapper<T>()))::type; };  decltypeされる為だけに存在している関数であれば、関数定 義は必要ない
    • その他付録 template<class T, std::size_t N> struct type_at { private: template<template<class...> class Type, class... Args> static auto impl(type_wrapper<Type<Args...>>) -> typename type_at_impl<N, type_wrapper<Args>...>::type; public: using type = typename decltype(impl(type_wrapper<T>()))::type; };  またコンパイル時でもdecltypeするだけであればconstexpr修 飾は必要無い
    • その他付録 むしろconstexpr修飾された関数は暗黙にinline修飾される上 inline修飾された関数の定義がない場合clang等は警告を出す のでconstexpr修飾はしないべき
    • その他付録 template<class T, std::size_t N> struct type_at { private: template<template<class...> class Type, class... Args> static auto impl(type_wrapper<Type<Args...>>) -> typename type_at_impl<N, type_wrapper<Args>...>::type; public: using type = typename decltype(impl(type_wrapper<T>()))::type; };  更にメタ関数を呼び出す
    • その他付録 template<std::size_t N, class... Types> struct type_at_impl { using type = decltype(type_at_impl_impl< make_type_sequence<(N)> >::eval(static_cast< Types* >(nullptr)...) ); }; type_sequence<void, void, void, ..., void>とN-1個のvoidを持つ 型を対数オーダーで生成してテンプレートパラメータとし て次の処理関数に渡す
    • その他付録 template<std::size_t N, class... Types> struct type_at_impl { using type = decltype(type_at_impl_impl< make_type_sequence<(N)> >::eval(static_cast< Types* >(nullptr)...) ); }; nullptrを受け取ってきた型リストの型のポインタにキャスト しながら展開して次の処理関数に引数として渡す
    • その他付録 template<std::size_t N, class... Types> struct type_at_impl { using type = decltype(type_at_impl_impl< make_type_sequence<(N)> >::eval(static_cast< Types* >(nullptr)...) ); }; Typesはtype_wrapper<T>形式の型が入ったパラメータパッ クなのでその要素は必ずポインタにキャスト出来る
    • その他付録 template<std::size_t N, class... Types> struct type_at_impl { using type = decltype(type_at_impl_impl< make_type_sequence<(N)> >::eval(static_cast< Types* >(nullptr)...) ); }; 更に実装の無い関数を呼び、decltypeを行う
    • その他付録 template<class... Types> struct type_at_impl_impl<type_sequence<Types...>> { template<class T> static T eval(Types*..., T*, ...); }; evalはテンプレートパラメータと関数引数の両方を受け取 るdecltypeされる事が目的の実装の無い関数
    • その他付録 template<class... Types> struct type_at_impl_impl<type_sequence<Types...>> { template<class T> static T eval(Types*..., T*, ...); }; TypesはN-1個のvoid型だからstatic_cast<Types*>(nullptr)...と してevalに渡された引数は手前からN-1個までのものはvoid* という扱いで受け取られる(type erasureが働く)
    • その他付録 template<class... Types> struct type_at_impl_impl<type_sequence<Types...>> { template<class T> static T eval(Types*..., T*, ...); }; Tはtype_wrapper<元のコンテナの型リストのN番目の型>に 推論される type_wrapperに包まれているのでN番目の型のcv修飾情報は 欠落しない
    • その他付録 template<class... Types> struct type_at_impl_impl<type_sequence<Types...>> { template<class T> static T eval(Types*..., T*, ...); }; 残りの余分な引数はellipsisに受け取られるので結果として 関数の戻り値型をTにすればdecltypeされた時にTを得る
    • その他付録 template<class T, std::size_t N> struct type_at { private: template<template<class...> class Type, class... Args> static auto impl(type_wrapper<Type<Args...>>) -> typename type_at_impl<N, type_wrapper<Args>...>::type; public: using type = typename decltype(impl(type_wrapper<T>()))::type; };  最終的に::typeでtype_wrapper<元のコンテナの型リストのN 番目の型>の中身である、元のコンテナの型リストのN番目 の型が得られる
    • その他付録 補足 decltype(expression)::typeはwell-formed
    • その他付録 この手法はNが大きい場合は有効であるが、 Nが小さい場合は複雑な実装のメタ関数より 線形実装の方が良い場合もある
    • その他付録 実際type_atではNが小さい場合が多そうなの で悩ましいという話になっている が、とりあえず次のrevisionでこのような問 題についても取り上げて貰える予定である 標準化による結果としてコンパイラマジック によるO(1)の実現というのもあり得るだろ うがやはりオーダー自体は重要である
    • その他付録 この方法は同様にしてint,int,int...などのパラ メータパック生成とconstexpr関数の組み合 わせでvalue_atの実装にも適用出来る (つまりvalue_atも対数オーダー実装が可能)
    • その他付録 実装は 怜-toki-などを見て欲しい https://github.com/fimbul/toki
    • おしながき 1. Templateの基本確認 2. Template Meta Programmingとは 3. Template Meta Programming入門 4. Template Meta Programming応用 5. その他付録 6. まとめ 7. 質疑応答
    • まとめ 今回、コンパイル時に行われるメタプログラ ミングのポテンシャル、型を扱うプログラ ミングというもののイメージ Template Meta Programmingの有用性 について少しでも伝わっていれば嬉しいです
    • まとめ Template Meta Programmingのメリット ・型を扱うプログラミングを行える ・型検査でバグを実行前の段階で検出する ・コンパイル時計算(実行時の処理を減らす) ・工夫でパフォーマンス向上(ET、遅延評価) など
    • まとめ Template Meta Programmingのデメリット ・酷使すればコンパイル時間が長くなる ・実装に実行時とは異なる技術知識が必要 ・不慣れだとデバッグがつらい(特にgcc)  (膨大なコンパイルエラーメッセージ) ・半分実装して途中経過を見るみたいな  事が難しいので脳内コンパイルも必要 など
    • まとめ デバッグ方法 ・static_assert ・実行時に結果を標準出力 ・実態のない型をインスタンス化する  (エラーメッセージで型を見る) ・mpl::print(必ずしも上手くはいかない) など
    • まとめ スライドの内容はTemplate Meta Programming の世界のたかだか氷山の一角に過ぎません
    • まとめ 世の中にはラムダ式(Boost.MPL)やレイト レーシング(Sprout)等もTMP(with constexpr) での実装例がありますし、Metaparseのよう な(闇の)ライブラリもあります (コンパイル時haskellコンパイラ的なサムシングなど)
    • まとめ 入門から応用までなどと謳ったものの結局 殆ど難しいことはろくに取り上げられず申 し訳ないです、簡単な内容だけで結構な量に なってしまっていたこともあり正直もっと 応用的な内容にまで手が回りませんでした
    • まとめ その実装だけで本一冊書けてしまう、TMP の世界で最も有名であろうBoost.MPLのよう なライブラリを一切取り上げられなかった のは残念です
    • まとめ 代わりに参考書籍を紹介します
    • まとめ Boost.MPLの解説書 ・主にMPLの実装にまつ  わる話など ・TMPの汎用テクニック ・やや情報は古い  (C++03向け) ・邦訳もあるはず
    • まとめ ・Template関連本で本当  によく紹介されてい  るので多分良書だと  思います ・こちらもC++03向け ・すいません自分は  まだ読めてません
    • まとめ TC++PL 4th Edition ・C++11に対応した最新  のTC++PL ・Templateの本というわ  けではない
    • まとめ スライドの誤り等あれば教えて下さい きっとバグあります…
    • まとめ ご清聴ありがとうございました 時間がおしていなければ質疑応答に移ります (おしてたら後で個人的にお願いします)
    • おしながき 1. Templateの基本確認 2. Template Meta Programmingとは 3. Template Meta Programming入門 4. Template Meta Programming応用 5. その他付録 6. まとめ 7. 質疑応答
    • 終わり ありがとうございました