読者です 読者をやめる 読者になる 読者になる

テンプレートの部分特殊化の引数リストにかかる制約

C++

昨日文字列を表す型を書いていて,このコードがエラーになるのが引っかかった.

template<class T, T... CS>
struct S{};



template<int I, class T>
struct X1;

template<class T, T C, T... CS>
struct X1<0, S<T, C, CS...>>{};

template<class T, int I, T... CS>
struct X1<I, S<T, CS...>>{};



template<int I, class T>
struct X2;

template<class T, T... CS>
struct X2<0, S<T, 'a', CS...>>{}; // C を 'a' に変えた

template<class T, int I, T... CS>
struct X2<I, S<T, CS...>>{};



int main()
{
    X1<0, S<char, 'a', 'b'>> x1; // OK.1つめの特殊化

    X2<0, S<char, 'a', 'b'>> x2; // NG. 1つめの特殊化
                                 // ambiguous class template instantiation in GCC

    return 0;
}

gcc 4.8.1 のエラー:

hoge2.cpp: In function 'int main()':
hoge2.cpp:32:30: error: ambiguous class template instantiation for 'struct X2<0, S<char, 'a', 'b'> >'
     X2<0, S<char, 'a', 'b'>> x2; // NG. 1つめの特殊化
                              ^
hoge2.cpp:21:8: error: candidates are: struct X2<0, S<T, 'a', CS ...> >
 struct X2<0, S<T, 'a', CS...>>{}; // C を 'a' に変えた
        ^
hoge2.cpp:24:8: error:                 struct X2<I, S<T, CS ...> >
 struct X2<I, S<T, CS...>>{};
        ^
hoge2.cpp:32:30: error: aggregate 'X2<0, S<char, 'a', 'b'> > x2' has incomplete type and cannot be defined
     X2<0, S<char, 'a', 'b'>> x2; // NG. 1つめの特殊化
                              ^
hoge2.cpp:30:30: warning: unused variable 'x1' [-Wunused-variable]
     X1<0, S<char, 'a', 'b'>> x1; // OK.1つめの特殊化
                              ^

Twitter で聞いてみたところ,@kikairoya さんに部分特殊化の制約に引っかかると教えてもらった. 該当箇所は 14.5.5 の8段落目で,テンプレートの部分特殊化の引数リストには次のような制約があるらしい.よく知らなかったのでメモ.飽くまで自分の解釈なので,もし間違っていたら指摘して貰えるとありがたいです.

テンプレートの部分特殊化の引数リストにかかる制約

  • 部分特殊化された非型引数には,単純に引数名だけを書く場合を除いて部分特殊化されたテンプレート引数を使ってはいけない.
template <int I, int J> struct A {};
template <int I> struct A<I+5, I*2> {}; // error.I は部分特殊化されたテンプレート引数

template <int I, int J> struct B {};
template <int I> struct B<I, I> {}; // OK.I は部分特殊化されたテンプレート引数だが,単純に引数名だけを使用している
  • 部分特殊化における非型引数の型は特殊化に使われた引数に依存してはいけない.今回ひっかかったのはこれ.
template <class T, T t> struct C {};
template <class T> struct C<T, 1>; // error.1 の型は T に依存している.

template< int X, int (*array_ptr)[X] > class A {};
int array[5];
template< int X > class A<X,&array> { }; // error.arrayの要素数は X に依存している

今回ひっかかったのはこの制約.

template<class T, T... CS>
struct X2<0, S<T, 'a', CS...>>{}; // 'a' の型が T に依存している
  • 特殊化の引数リストは一次テンプレートの暗黙の引数(デフォルト引数が指定されている引数?)によって一致されるようになっていてはいけない

  • 特殊化のテンプレート引数はデフォルト引数を持っていてはいけない.

  • 引数に予期しないパラメータパックを含んでいてはいけない(パラメータパックの展開は最後の引数で行わなければいけない)

分かっていないこと

  • gcc のエラーメッセージが ambiguous class template instantiation であるのはなぜか(今回の制約に違反していることに対するエラーではない)

  • clang だと最初のコードが普通に通ってしまうのはなぜか