Consider the following program:
template<template<typename ...> class>
struct foo {};
template<template<typename> class C>
struct foo<C> {};
int main() {}
Clang rejects it with error:
class template partial specialization does not specialize any template argument
even in latest clang 7.0 HEAD, see demo here. However, gcc accepts it.
Refer to [temp.class.spec] where the rules of partial specialization are stated, I couldn't find anything that prohibits the partial specialization of this template. Especially, the specialization is indeed more specialized, the error message looks incorrect.
EDIT:
However, gcc's behavior is also abnormal, consider the following program:
#include <iostream>
template<template<typename ...> class>
struct foo { void show() { std::cout << "Primary.\n"; } };
template<template<typename> class C>
struct foo<C> { void show() { std::cout << "Specialized.\n"; } };
template<class...> struct bar {};
int main() {
foo<bar> f;
f.show();
}
It turns out that gcc uses the specialized version in this case, see here.
Now I want to ask:
is this kind of partial specialization allowed by standard ?
which compiler is correct ? ( one/all/none of them ? )
Template classes and functions can make use of another kind of template parameter known as a non-type parameter. A template non-type parameter is a template parameter where the type of the parameter is predefined and is substituted for a constexpr value passed in as an argument.
Variadic templates are class or function templates, that can take any variable(zero or more) number of arguments. In C++, templates can have a fixed number of parameters only that have to be specified at the time of declaration. However, variadic templates help to overcome this issue.
The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.
With the variadic templates feature, you can define class or function templates that have any number (including zero) of parameters. To achieve this goal, this feature introduces a kind of parameter called parameter pack to represent a list of zero or more parameters for templates.
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With