This finaly seems to be a GCC bug in the implementation of the new C++ template template argument deduction partial support for this feature.
To determine if a partial specialization is more specialized than the class template, partial ordering is applied to 2 corresponding synthetized functions:
//template class:
template<template<class...>class P> void f_foo0(foo<P>);
//Partial specialization
template<template<class P> class P> void f_foo_partial0(foo<P>);
Then it is attempted to perform template argument deduction, by calling each functions with an argument corresponding to the other function parameter (see [temp.func.order])
template<class P> struct Atype{};
template<class ...P> struct ATypePack{};
//Is f_foo_partial at least as specialized as f_foo?
f_foo(foo<AType>{});
//Is f_foo at least as specialized as f_foo_partial?
f_foo_partial(foo<ATypePack>{});
If template argument succeed for (1) then f_foo_partial
is at least as specialized as f_foo
. If template argument succeed for (2) then f_foo
is at least as specialized as f_foo_partial
.( see [temp.deduct.partial]). Then if only one is at least as specialized as the other, then it is the one which is more specialized.
So to check if template argument is deductible, then deduction of template argument from a type is perfomed.
Then to perform this matching, the introduced rule in C++17 is applied [temp.arg.template]/3:
A template-argument matches a template template-parameter P when P is at least as specialized as the template-argument A.[...]
And [temp.arg.template]/4 specify that this ordering will be performed similarily to the preceding case using these invented two functions:
template<class...> struct X{};
//for the argument
template<class...P> void f_targ(X<P...>);
//Partial specialization
template<class P> void f_tparam(X<P>);
struct Atype{};
struct ATypePack{};
//Is template template parameter at least as specialized template template arg?
f_targ(X<AType>{});
//Is template template arg at least as specialized as template template parameter?
f_tparam(X<ATypePack>{});
for (1) template argument succeed the deduced argument for ...P`` is
AType`.
for (2) there is a special rule, that applies only in the case of template partial ordering [temp.deduct.type]/9.2:
[...] During partial ordering, if Ai was originally a pack expansion:
if P does not contain a template argument corresponding to Ai then Ai
is ignored;
otherwise, if Pi is not a pack expansion, template argument deduction fails.
Here Ai is ATypePack
and Pi
is the P
in the function argument of template<class P> void p_foo_partial(foo<P>)
.
So due to this rule cited in bold, template argument deduction fails for (2), so template template parameter "class P" is more specialized than its argument. So the call f_foo_partial(foo<ATypePack>{})
is well formed.
On the other hand the same rules, the call to f_foo(AType{})
is well formed because of [temp.arg.temp]/3:
[...] 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.[...]
so f_foo_partial
is not more specialized than f_foo
, so template<class<class > class C> struct foo<C>;
is not a partial specialization of template foo
.