C++17のクラスのテンプレート実引数推定
テンプレートは以下のように定義する。
template < typename T >
class C
{
C ( T t ) { }
} ;
template < typename T >
void f( T t ) { }
テンプレートは以下のように使う。
int main()
{
C<int> c(42) ;
f<int>(42) ;
}
しかし、C++プログラマーの大半は、関数テンプレートfをこのように使うことはない。大抵は、f(42)のように使う。このように、テンプレート名を書いた場合、C++コンパイラーはテンプレートの実引数推定と呼ばれる仕組みを使ってテンプレート実引数を推定する。
その詳しい仕組みは難解だが、考え方としてはこうだ。42の型はintである。すると、tの型はintである。tの型はTとされているので、Tはintである。Tはテンプレート仮引数である。するとテンプレート仮引数Tに対する実引数はintである。
テンプレートの実引数推定は、残念ながらC++14ではクラステンプレートには存在しない。しかし、上の例をみるように、クラステンプレートにもコンストラクターがあるのだから、適用できるはずだ。
C++17ではまさにクラステンプレートに実引数推定ができるようになった。以下のように書ける。
C c1(42) ;
C c2 = 42 ;
C++17では、実引数とクラステンプレートCのコンストラクターの仮引数から、クラステンプレートのテンプレート実引数を推定できるようになる。
しかし、C c(42)という形はもっとも簡単なものだ。現実には、以下のような場合もある。
template < typename T >
class C
{
template < typename Iterator >
C ( Iterator begin, Iterator end ) ;
} ;
int main()
{
int a[] = { 1,2,3,4,5 } ;
// エラー、IteratorからTは推定できない
C c( std::begin(a), std::end(a) ) ;
}
この場合、IteratorからTは推定できないのでエラーとなる。
この問題を解決するために、C++17では、推定ガイド(deduction guide)という文法が追加される。以下のように書けば、IteratorからTが推定できる。
template < typename T >
class C
{
template < typename Iterator >
C ( Iterator begin, Iterator end ) ;
} ;
template < typename Iterator >
C( Iterator begin, Iterator end )
-> C< std::iterator_traits<Iterator>::value_type >
int main()
{
int a[] = { 1,2,3,4,5 } ;
C c( std::begin(a), std::end(a) ) ;
}
文法は以下の通り。
テンプレート名 (引数リスト) -> 実引数付きのテンプレート名