はじめに
いつものパターンでテンプレートの型推論について何回も忘れるのでメモする。
参考書籍はEffective Modern C++。
パターン分け
テンプレートの型推論について、下記関数テンプレートの擬似コードを用いて3パターンに分類。
template<typename T>
void f(ParamType param);
f(expr); // 実引数exprからTとParamTypeを推論
- ParamTypeが参照 or ポインタ (ユニバーサル参照ではない)
- ParamTypeがユニバーサル参照
- ParamTypeが参照でもポインタでもない
パターン1: ParamTypeが参照 or ポインタ (ユニバーサル参照ではない)
下記条件に従って推論する。
- exprの参照性は無視される
- exprの型をParamTypeとパターンマッチングし、Tを決定
template<typename T>
void f(T& param); // ParamType: T&
int x = 27;
const int cx = x;
const int& rx = x;
f(x); // f(int& param) : Tはint
f(cx); // f(const int& param): Tはconst int
f(rx); // f(const int& param): Tはconst int
template<typename T>
void f(const T& param); // ParamType: const T&
int x = 27;
const int cx = x;
const int& rx = x;
f(x); // f(const int& param): Tはint
f(cx); // f(const int& param): Tはint
f(rx); // f(const int& param): Tはint
template<typename T>
void f(T* param); // ParamType: T*
int x = 27;
const int* px = &x;
f(&x); // f(int* param) : Tはint
f(px); // f(const int* param): Tはconst int
パターン2: ParamTypがユニバーサル参照
下記条件に従って推論する。
- exprが左辺値の場合、TもParamTypeは左辺値参照と推論
- exprが右辺値の場合、パターン1の規則を適用
template<typename T>
void f(T&& param); // ParamType: T&&
int x = 27;
const int cx = x;
const int& rx = x;
f(x); // f(int& && param) -> f(int& param): 参照の圧縮
// f(int& param) : Tはint&
f(cx); // f(const int& && param) -> f(const int& param): 参照の圧縮
// f(const int& param): Tはconst int&
f(rx); // f(const int& && param) -> f(const int& param): 参照の圧縮
// f(const int& param): Tはconst int&
f(27); // f(int&& param): Tはint
パターン3: ParamTypeが参照でもポインタでもない
下記条件に従って推論する。
- exprの参照性は無視される
- cv型修飾子(const, volatile)も無視される
template<typename T>
void f(T param); // ParamType: T
int x = 27;
const int cx = x;
const int& rx = x;
f(x); // f(int param): Tはint
f(cx); // f(int param): Tはint (paramは実引数のコピーなのでconstは付かない)
f(rx); // f(int param): Tはint
const char* const ptr = "const pointer";
f(ptr); // f(const char* param): Tはconst char*
実引数が配列の場合はポインタに成り下がる(decay)ので注意。
template<typename T>
void f(T param); // ParamType: T
const char ary[] = "char array";
f(ary); // f(const char* param): Tはconst char*
ただし、ParamTypeが参照の場合は成り下がらない。
template<typename T>
void f(T& param); // ParamType: T
const char ary[] = "char array";
f(ary); // f(const char (&)[11] param): Tはconst char [11]