以下のコードについて考える.
# include <vector>
template< typename ValueType, template< class, class > class Container >
class c
{
private:
Container< ValueType > m_container;
};
int main() { c< int, std::vector >(); }
上記のコードは, std::vector
の第 2 テンプレート型引数に対してデフォルトテンプレート引数 std::allocator< ValueType >
が渡される事を期待したコードであるが, 引数を 2 つ取るクラステンプレート Container
に対して 1 つしかテンプレート引数を渡していない, というビルドエラーとなる.
ここで, 関数 main
の定義を以下のように書き換える.
int main() {}
これでもエラーは解消しない. これはクラステンプレート c
に対して実テンプレート引数を与えなく4てもエラーになる, 即ちクラス c
のデータメンバ m_container
の名前探索がクラステンプレートの定義時に行われている事を示している.
N3797 14.6 Name resolution 項 9 に拠れば
When looking for the declaration of a name used in a template definition, the usual lookup rules (3.4.1, 3.4.2) are used for non-dependent names. The lookup of names dependent on the template parameters is postponed until the actual template argument is known (14.6.2).
とあるので, データメンバ c::m_container
の名前探索は, 実際にクラステンプレート c
に対してテンプレート実引数を与えた時まで遅延されなければならないと思うのだが, 構文解析の都合でエラーとしている物と思われる. 規格上, 上記のコードをエラーとする根拠は分からなかった.
ただし, 規格上同等のコードは以下のように書く事が想定されている.
# include <vector>
template< typename ValueType, template< class > class Container >
class c
{
private:
Container< ValueType > m_container;
};
int main() { c< int, std::vector >(); }
上記のコードは gcc 8.0.0, gcc 7.1.0 ではビルドが通る. clang では通らない. 上記の書き方については, 古い規格と新しい規格とで異なっている.
以下は N3797 14.3.3 Template template arguments 項2 より引用.
template<class T> class A { /∗ ... ∗/ };
template<class T, class U = T> class B { /∗ ... ∗/ };
template<class ... Types> class C { /∗ ... ∗/ };
template<template<class> class P> class X { /∗ ... ∗/ };
template<template<class ...> class Q> class Y { /∗ ... ∗/ };
X<A> xa; // OK
X<B> xb; // ill-formed: default arguments for the parameters of a template argument are ignored
X<C> xc; // ill-formed: a template parameter pack does not match a template parameter
Y<A> ya; // OK
Y<B> yb; // OK
Y<C> yc; // OK
以下は N4659 17.3.3 Template template arguments 項 3 より引用.
template<class T> class A { /* ... */ };
template<class T, class U = T> class B { /* ... */ };
template<class ... Types> class C { /* ... */ };
template<auto n> class D { /* ... */ };
template<template<class> class P> class X { /* ... */ };
template<template<class ...> class Q> class Y { /* ... */ };
template<template<int> class R> class Z { /* ... */ };
X<A> xa; // OK
X<B> xb; // OK
X<C> xc; // OK
Y<A> ya; // OK
Y<B> yb; // OK
Y<C> yc; // OK
Z<D> zd; // OK
N3797 の 14.3.3 と N4659 の 17.3.3 を比較すると, N4659 では項 4 が追加されている他, 項 3 の前半が異なる.
N3797 では
A template-argument matches a template template-parameter (call it P) when each of the template parameters in the template-parameter-list of the template-argument’s corresponding class template or alias template (call it A) matches the corresponding template parameter in the template-parameter-list of P.
となっている部分が N4659 では
A template-argument matches a template template-parameter P when P is at least as specialized as the template-argument A. If P contains a parameter pack, then A also matches P if each of A’s template parameters matches the corresponding template parameter in the template-parameter-list of P.
となっている. この "at least as specialized as the template-argument A" と書かれている部分をどう解釈すべきかが曲者で, C++ 的に「特殊化」と考えたくなるがここは「適合させる」と考えるべきであろう.