SlideShare a Scribd company logo
templateとautoの型推論
2015/1/28 EMC1
光成滋生(@herumi)
このテキストでの記法
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
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
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
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
exprがrvalueなら一つ目と同じ規則
exprがlvalueならTとParamTypeはlvalue参照で
あるかのように推論される
Tが参照型になるのはこのときだけ
ParamTypeはrvalue参照であるかのように定義される
が型はlvalue参照になる
exprがlvalueかrvalueかによって違うのが重要
ParamTypeがuniversal参照
6/22
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
値渡しになる
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
exprが差す先のconst性は無視されない
ParamTypeがポインタでも参照でもない
template<class T> void f(T param);
const char *const p = "abc";
f(p); // T = param = const char *
9/22
ポインタと配列は異なる型
大抵は配列は先頭要素へのポインタに変換される
関数パラメータには配列型がない
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
配列への参照は変換されない
配列のサイズを取得するテクニック
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
関数型も関数ポインタに変換される
関数型への参照は変換されない
関数
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
 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
参照型の参照は無視される
universal参照はlvalueとrvalueでルールが異なる
値渡しではconst, volatileは無視される
arrayや関数は参照でないならポインタになる
Item 1のまとめ
14/22
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
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
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}; // エラー
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}; // エラー
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
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
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
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

More Related Content

templateとautoの型推論