LoginSignup
5
1

More than 5 years have passed since last update.

テンプレートテンプレートパラメータでデフォルト引数を使うとコンパイルエラーになるのは準拠する規格が古いため

Posted at

以下のコードについて考える.

#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++ 的に「特殊化」と考えたくなるがここは「適合させる」と考えるべきであろう.

5
1
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1