参照配列の特殊化と型推論

C++テンプレートテクニックを読んでいて見慣れない構文が出てきた

template<class T,std::size_t N>
struct range_iterator< T[N] > {
    typedef T* type;
};

このT[N]で特殊化してる部分が良く分からなかったので自分なりに色々調査してみた。

まずはどういうときに呼ばれるのか試してみる。

#include <iostream>
using std::cout; using std::endl;

template<class T>
struct value_test {
    static const int type = 1;
};

template<class T,std::size_t N>
struct value_test< T[N] > {
    static const int type = 2;
};

template<class T>
struct value_test< T* > {
    static const int type = 3;
};

int main () {
    cout << value_test<int>::type     << endl;
    cout << value_test<int[10]>::type << endl;
    cout << value_test<int*>::type    << endl;
}

これを実行すると

$ main
1
2
3

となった。つまりint[10]のようなものを渡せばT[N]となるようだ。ちなみにT[N]をT&にしてもちゃんと動く。両者の違いとしては恐らくTが配列の参照の場合のみの特殊化としてインスタンス化できるところにあるのかな。

さて、ここまではわかったのものの、「value_test::type」なんて書き方をあまりしないものだと思われる。

恐らく殆どの場合、型推論によって「value_test::type」と解釈されるという使い方が一般的なのではなかろうか。

#include <iostream>
using std::cout; using std::endl;

template<class T>
struct value_test {
    static const int type = 1;
};

template<class T,std::size_t N>
struct value_test< T[N] > {
    static const int type = 2;
};

template<class T>
struct value_test< T* > {
    static const int type = 3;
};

template<class T>
int func (T&) {
    return typename value_test<T>::type;
}

int main () {
    int a;
    int b[10];
    int* c;
    
    cout << func(a) << endl;
    cout << func(b) << endl;
    cout << func(c) << endl;
}
$ main
1
2
3

上記のようなfuncというテンプレート関数を用意する。戻り値は「value_test::type」としておく。するとfuncの呼び出し方によって型推論が働き、b[10]を引数として渡したfuncの場合にT[N]の特殊化がインスタンス化されるということになる。

凄く面白い。けど、頭がスクランブルエッグだ。