Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.

templateとautoの型推論

12.724 Aufrufe

Veröffentlicht am

Effective Modern C++勉強会資料
https://github.com/herumi/emcjp

Veröffentlicht in: Technologie
  • Login to see the comments

templateとautoの型推論

  1. 1. templateとautoの型推論 2015/1/28 EMC1 光成滋生(@herumi)
  2. 2. このテキストでの記法 T : templateの型 ParamType : Tを含む複雑な型 ParamType = T, const T&, T*, ... templateの型推論は3パターン ParamTypeがポインタ型かuniversalでない参照型 universalな参照型 詳細はItem 24で(右辺値参照:以降univ参照と表記) その他 Item 1 : templateの型推論 // 宣言 template<class T> void f(ParamType param); // 呼び出し f(expr); 2/22
  3. 3. exprが参照型なら参照部分を取り除く ParamTypeにしたがってTを決める ParamType = T&のときの例 constオブジェクトを関数に渡しても値は変わらないべき ParamTypeがポインタかunivでない参照 template<class T> void f(T& param); int x = 27; const int cx = x; const int& rx = x; f(x); // T = int, param = int& f(cx); // T = const int, param = const int& f(rx); // T = const int, param = const int& 3/22
  4. 4. ParamType = const T&のときの例 rxの参照は無視されるのでx, cxと同じ ParamTypeがポインタかunivでない参照 template<class T> void f(const T& param); int x = 27; const int cx = x; const int& rx = x; f(x); // T = int, param = const int& f(cx); // T = int, param = const int& f(rx); // T = int, param = const int& 4/22
  5. 5. ParamType = T*のときの例 参照とポインタは本質的に同じ ParamTypeがポインタかunivでない参照 template<class T> void f(T* param); int x = 27; const int* px = &x; f(&x); // T = int, param = int* f(px); // T = const int, param = const int* 5/22
  6. 6. exprがrvalueなら一つ目と同じ規則 exprがlvalueならTとParamTypeはlvalue参照で あるかのように推論される Tが参照型になるのはこのときだけ ParamTypeはrvalue参照であるかのように定義される が型はlvalue参照になる exprがlvalueかrvalueかによって違うのが重要 ParamTypeがuniversal参照 6/22
  7. 7. ParamType = T&&のときの例 参照とポインタは本質的に同じ ParamTypeがuniversal参照 template<class T> void f(T&& param); int x = 27; const int cx = x; const int& rx = x; f(27); // rvalue : T = int, param = int&& f(x); // lvalue : T = int&, param = int& f(cx); // lvalue : T = const int&, param = const int& f(rx); // lvalue : T = const int&, param = const int& 7/22
  8. 8. 値渡しになる paramはコピーされる 参照, const, volatileは無視される ParamTypeがポインタでも参照でもない template<class T> void f(T param); int x = 27; const int cx = x; const int& rx = x; f(x); // T = param = int f(cx); // T = param = int f(rx); // T = param = int 8/22
  9. 9. exprが差す先のconst性は無視されない ParamTypeがポインタでも参照でもない template<class T> void f(T param); const char *const p = "abc"; f(p); // T = param = const char * 9/22
  10. 10. ポインタと配列は異なる型 大抵は配列は先頭要素へのポインタに変換される 関数パラメータには配列型がない templateも同様 配列 const char name[] = "abc"; // const char[4] const char *p = name; // const char* void f(int *param); void f(int param[]); // int *paramと同じ template<class T> void f(T param); f(name); // T = const char * 10/22
  11. 11. 配列への参照は変換されない 配列のサイズを取得するテクニック C++11ならarray<T, N>がある 注意:array<T,3>::iteratorとarray<T,5>::iteratorは同じ型かも 配列への参照 const char name[] = "abc"; // const char[4] template<class T> void f(T& param); f(name); // T = const char (&)[4] template<class T, size_t N> constexpr size_t arraySize(const T (&)[N]) noexcept { return N; } 11/22
  12. 12. 関数型も関数ポインタに変換される 関数型への参照は変換されない 関数 void g(int); template<class T>void f1(T param); template<class T>void f2(T& param); f1(g); // T = void (*)(int); f2(g); // T = void (&)(int); 12/22
  13. 13.  template<class T> void f(Para para)の分類  関数f(Para)にXを渡したときのTの型 \ 関数 \ 渡す型X f(T) f(T&) f(const T&) f(T&& para) T= para T& =para const T& =para T para int int int int int& int& int& int int int int& int& const int int const int int const int& const int& const int& int const int int const int& const int& 1 int error int int int&& 一覧表 13/22
  14. 14. 参照型の参照は無視される universal参照はlvalueとrvalueでルールが異なる 値渡しではconst, volatileは無視される arrayや関数は参照でないならポインタになる Item 1のまとめ 14/22
  15. 15. autoの型推論は殆どtemplateの型推論と同じ autoはParamTypeとして振る舞う 参照をつけないと値はコピーされる Item 2 : autoの型推論 auto x = 27; は template<class T> void f_for_x(T param); f_for_x(27); の挙動と等しい。 const auto& rx = x; は template<class T> void f_for_rx(const T& param); f_for_rx(x); の挙動と等しい。 15/22
  16. 16. Item 1の分類に応じる autoの推論例 auto x = 27; // int const auto cx = x; // const int const auto& rx = x; // const int& auto&& ur1 = x; // xはintでlvalueなのでint& auto&& ur2 = cx; // cxはconst intでlvalueなのでconst int& auto&& ur3 = 27; // 27はintでrvalueなのでint&& const char name[] = "abc"; auto a1 = name; // const char * auto& a2 = name; // const char (&)[4]; void func(int, double); auto f1 = func; // void (*)(int, double); auto& f2 = func; // void (&)(int, double); 16/22
  17. 17. autoは一つの要素でも初期化リストになる template関数に直接渡せない 初期化リストの注意点(1/2) int x1(3); // x1 = 3 int x2{5}; // x2 = 5 auto a1(3); // int a1 = 3; auto a2{5}; // std::initializer_list<int> a2 = {5}; template<class T> void f(T param); auto a{1, 2, 3}; f(a); // ok f({1, 2, 3}); // なぜかerror template<class T> void f(std::initializer_list<T> param); f({1, 2, 3}); // これはok 17/22 C++17から変わる(N3922) auto x = {1}; // 初期化リスト auto x = {2, 3, 4}; // 初期化リスト auto x{1}; // int auto x{2, 3, 4}; // エラー
  18. 18. autoがtemplate型の推論型になるところ ラムダ関数のauto引数 C++14のreturnのauto クイズ これは? 初期化リストの注意点(2/2) auto f() { return {1, 2}; // error } const auto x{2, 3, 4}; auto f = [&](auto x) { return x.size(); } f(x); // ok f({1, 2}); // error 18/22 auto x{0xf, 0xffffffff}; auto y{4294967295, 0xffffffff}; C++17から変わる(N3922) const auto x = {1}; // 初期化リスト const auto x = {2, 3, 4}; // 初期化リスト const auto x{1}; // int const auto x{2, 3, 4}; // エラー
  19. 19. autoを使うと無駄なコピーが発生するかも std::vector<int> v = vs[i]; とコピー forとautoの組み合わせの注意点(1/4) std::vector<std::vector<int>> vs; for (auto v : vs) { ... } std::vector<std::vector<int>> vs; for (auto& v : vs) { ... } // 値を変更するとき for (const auto& v : vs { ... } // 値をいじらないとき 19/22
  20. 20. std::vector<bool>に対してauto&はエラー vector<bool>は一時オブジェクトを生成するため auto&&を使う auto&&は先程の例でもうまくいく いつでもauto&&でええんじゃないか auto&&を省略してしまってもいいんじゃないか 規格に提案されclang/VCで実装されたが却下される forとautoの組み合わせの注意点(2/4) std::vector<bool> v; for (auto& e : v) // エラー for (auto&& e : v) // うまくいく 20/22
  21. 21. const auto&よりautoがよいレアケース forとautoの組み合わせの注意点(3/4) void fff(int *y, std::array<int, 2>& v) { for (const auto& x : v) { *y += x; *y += x; } } movl (%rdi), %eax ; eax ← y addl (%rsi), %eax ; eax ← v[0] movl %eax, (%rdi) ; yを更新 addl (%rsi), %eax ; eax ← v[0] もう一度値を読む movl %eax, (%rdi) addl 4(%rsi), %eax ; eax ← v[1] movl %eax, (%rdi) addl 4(%rsi), %eax ; eax ← v[1] もう一度読む movl %eax, (%rdi) retq 21/22
  22. 22. yの更新でxが変わる可能性を考慮するため autoにして値は変わらないとコンパイラに教える 注意 : この場合はyの一時変数を導入するのが一番よい forとautoの組み合わせの注意点(4/4) void fff(int *y, std::array<int, 2>& v) { for (auto x : v) { *y += x; *y += x; } } movl (%rsi), %eax ; eax ← v[0] addl %eax, %eax ; eaxを2倍して addl (%rdi), %eax ; yを足す movl %eax, (%rdi) ; yに書き戻す movl 4(%rsi), %ecx ; ecx ← v[1] leal (%rax,%rcx,2), %eax ; ecxの2倍を足す movl %eax, (%rdi) retq 22/22

×