Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

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.
"; } };

template<template<typename> class C>
struct foo<C> { void show() { std::cout << "Specialized.
"; } };

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 ? )

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
387 views
Welcome To Ask or Share your Answers For Others

1 Answer

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`` isAType`.

  • 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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...