クラステンプレート型の推論は定義を緩くすると昔からありますた…
C++17 からはその推論機能が大幅に強化されました。
それでどういう機能かと言うと、テンプレートを指定しなくても、自動で推論してくれます。
別の言葉を使って説明すると、テンプレートの引数を指定もしていないのに、テンプレートの型を推論してくれるのがクラステンプレートの型推論です。
といっても使い勝手が少しよくなったぐらいで C++17 の界隈でどれだけ需要があるかは疑わしいです。
なぜそう思うかと言うと最近使われているスクリプト言語に近い構文が使えるようになるという程度です。
コンパイラ依存もまだあるので、すぐに使えるというほどではないですし、導入されるのは数年後ぐらいの印象ですかね。
ではまず強化された型推論の例でも見てみましょうかね。
1 #if __cplusplus < 201703L 2 # pragma message "-std=c++17を指定してください" 3 #endif 4 5 #if defined(__GNUC__) && (__GNUC__ < 7) 6 # ifndef __clang__ 7 # pragma message "gccのバージョンが古いです" 8 # endif 9 #elif defined(__clang__) && (__clang_major__ < 6) 10 # pragma message "clangのバージョンが古いです" 11 #endif 12 13 #include <type_traits> 14 15 template<class T> 16 struct A { 17 A(T){} 18 A(const A&){} 19 }; 20 21 #if __cplusplus >= 201703L 22 template<class T> 23 A(T) -> A<T>; 24 #endif 25 26 int main() 27 { 28 29 #if __cplusplus >= 201703L 30 A a(10); 31 A b = a; 32 A c(a); 33 34 #else 35 A<int> a(10); 36 A<int> b = a; 37 A<int> c(a); 38 #endif 39 40 static_assert(std::is_same<decltype(a),A<int>>::value,""); 41 static_assert(std::is_same<decltype(b),A<int>>::value,""); 42 static_assert(std::is_same<decltype(c),A<int>>::value,""); 43 44 return 0; 45 }
ちょっと C++17 と C++14 以前でも通すために無理をしていますが、重要なのはクラステンプレートのインスタンス化をするのにテンプレートパラメーターが不要な点です。
C++14 以前の構文では以下のようにテンプレート引数を指定していますね。
15 template<class T> 16 struct A { 17 A(T){} 18 A(const A&){} 19 }; //割愛 35 A<int> a(10); 36 A<int> b = a; 37 A<int> c(a);
これは C++17 では以下のような構文に書き直すことができます。
15 template<class T> 16 struct A { 17 A(T){} 18 A(const A&){} 19 }; //割愛 22 template<class T> 23 A(T) -> A<T>; //割愛 30 A a(10); 31 A b = a; 32 A c(a);
このようにテンプレート引数なしでインスタンス化できるため、文字を簡略できますね。
ただしこの構文を可能とするには推論ガイドという定義をしてやる必要があります。
それが以下の2行です。
22 template<class T> 23 A(T) -> A<T>;
これは T を引数とする A のコンストラクタ A(T) は A<T> として解釈ができるよ〜というコンパイラにヒントを与えています。
クラステンプレート型推論とはこんな風にガイドを与える必要がありますが、STLコンテナの中にはすでにガイドが提供されているものがあります。
例えば std::pair が良い例かと思います。
1 #if __cplusplus < 201703L 2 # pragma message "-std=c++17を指定してください" 3 #endif 4 5 #include <utility> 6 #include <iostream> 7 8 int main() 9 { 10 #if __cplusplus >= 201703L 11 std::pair x{1,2}; 12 #else 13 std::pair<int,int> x{1,2}; 14 #endif 15 std::cout << std::get(1)(x) << '\n'; 16 return 0; 17 }
この例ではテンプレート推論のガイドを定義してなくても推論が出来てますね。
11 std::pair x{1,2};
C++14 以前の構文を考えるとかなり便利になったと言えるかもしれません。
13 std::pair<int,int> x{1,2};
std::pair は他のSTLコンテナと使う事があるので、このように手軽に使える構文は悪くはないです。
他にも std::tuple も同じ使い方が可能となっています。
std::pair の他にもテンプレート引数での型が指定したものはいくつかありますが std::initializer_list がその最たるものだと思います。
1 #if __cplusplus < 201703L 2 # pragma message "-std=c++17を指定してください" 3 #endif 4 5 #include <initializer_list> 6 #include <iostream> 7 8 struct B { }; 9 10 template<class T> 11 struct A 12 { 13 A(T,B){} 14 A(T,T){} 15 A(const A&){} 16 A(std::initializer_list<T>){} 17 }; 18 19 #if __cplusplus >= 201703L 20 template<class T> 21 A(T,B) -> A<T>; 22 23 template<class T> 24 A(T,T) -> A<T>; 25 26 template<class T> 27 A(std::initializer_list<T>) -> A<T>; 28 #endif 29 30 int main() { 31 A a(1,2); 32 A b(1,B{}); 33 A c{1}; 34 A d(a); 35 #if defined(___GNUC__) && (__GNUC__ >= 7) 36 # ifndef __clang__ 37 std::initializer_list il{1,2,3}; 38 A e(il); 39 # endif 40 #elif defined(__clang__) && (__clang_major__ <= 6) 41 std::initializer_list<int> il{1,2,3}; 42 A e(il); 43 #endif 44 return 0; 45 }
gcc7.3 では以下の箇所は通ります。
37 std::initializer_list il{1,2,3}; 38 A e(il);
ただこの構文を clang6.0 で試したところコンパイルに失敗してしまいました。
この例のように型推論はかなり発展途上らしくコンパイラ依存の部分が結構あるようです。
コンパイラ依存についてはもう一つ例をお見せしたいので次の項目をご覧ください。
Copyright 2017-2018, by Masaki Komatsu