initializer listはテンプレート実引数推定されない
initializer listは、テンプレート仮引数がstd::initializer_list<T>
でない限り、テンプレート実引数推定で、std::initializer_list
を導かない。
14.8.2.5/5 Deducing template arguments from a type
A function parameter for which the associated argument is an initializer list (8.5.4) but the parameter does not have std::initializer_list or reference to possibly cv-qualified std::initializer_list type.
なので次のコードでは、テンプレート実引数の推定に失敗し、他にオーバーロードされた関数がなければ、関数呼び出しに失敗する。
template <typename T>
void f (T) {}
int main () {
f({1, 2, 3}); // f<std::initializer_list<int>>が呼ばれて欲しいが、コンパイルエラー。
}
ちなみに、関数の戻り値型の推定でもstd::initializer_list
は導出できない。
変数宣言でのauto
指定子を使った型推定では、std::initializer_list
を導出できる。
対策
テンプレート実引数推定に頼らなければ良い。
template <typename T = std::initializer_list<int>>
void f (T) {}
int main () {
f({1, 2, 3}); // テンプレート実引数の推定に失敗するが、デフォルトテンプレートパラメタが代入される。
}
ただし、この方法では、std::initializer_list
のテンプレートパラメタを固定しなければならない。
次の様にすれば、テンプレートパラメタを固定するタイミングを関数呼び出し時にまで遅らせる事ができる。
template <typename S, typename T = std::initializer_list<S>>
void f (T) {}
int main () {
f<int>({1, 2, 3}); // std::initializr_listのテンプレートパラメタを指定する。
}
ただし、いずれにしてもユーザーが手で型を指定しなければならないので、使い辛い。
上手い方法があれば教えて欲しい。