2016-10-11

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) ) ;
}

文法は以下の通り。

テンプレート名 (引数リスト) -> 実引数付きのテンプレート名

2 comments:

Anonymous said...

<int>が消えてるようです.

Anonymous said...

ますますJS化していきますね。
Lispはブラックホールだ。って言葉を思い出します。
まぁ、ANYを欲しているわけなのですが。