(Partially) Specializing a Non-Type Template Parameter of Dependent Type

(Partially) specializing a non-type template parameter of dependent type

See paragraph [temp.class.spec] 14.5.5/8 of the standard:

The type of a template parameter corresponding to a specialized
non-type argument shall not be dependent on a parameter of the
specialization. [ Example:

template <class T, T t> struct C {};
template <class T> struct C<T, 1>; // error

template< int X, int (*array_ptr)[X] > class A {};
int array[5];
template< int X > class A<X,&array> { }; // error

—end example ]

The answer to your edit: the easiest workaround is to replace a non-type template parameter with a type one:

#include <type_traits>

template <typename T, typename U>
struct X_;

template <typename T, T N>
struct X_<T, std::integral_constant<T, N>> {};

template <typename T>
struct X_<T, std::integral_constant<T, 0>> {};

template <typename T, T N>
struct X : X_<T, std::integral_constant<T, N>> {};

Partial specialization of single type template parameter class template using non-type template parameter through a dependent type

As far as I can tell, the first snippet is ill-formed (and a diagnostic is required); compilers should reject the program because of the partial specialization (2).

[temp.deduct.type]/18 applies here:

If P has a form that contains <i>, and if the type of i differs
from the type of the corresponding template parameter of the template
named by the enclosing simple-template-id, deduction fails. [...]

The associated example in the Standard uses a function template, but is otherwise very similar.

So the template argument of the partial specialization (2) can never be deduced, and [temp.class.spec.match]/3 applies:

If the template arguments of a partial specialization cannot be
deduced because of the structure of its template-parameter-list and
the template-id, the program is ill-formed.


Interestingly, I couldn't find a compiler that diagnoses this issue, not even EDG in strict mode. We could speculate that most compiler writers consider the benefits of having a diagnostic here not to be worth the effort of implementing the checks. This could mean that we might see the requirement in the paragraph above change in the future from ill-formed to ill-formed, no diagnostic required. However, this is pure speculation. In any case, I don't see it ever changing to well-formed; I can't think of a valid use for a partial specialization that never matches.


The wording of [temp.deduct.type]/18 was clarified by the resolution of CWG2091.

C++ Class partial specialization on non-type template parameter

Let's consider what the standard (working draft) says:

A member [...] of a class template may be explicitly specialized for a given implicit instantiation of the class template, even if the member [...] is defined in the class template definition. An explicit specialization of a member [...] is specified using the syntax for explicit specialization.

In other terms, what you are trying to do is not allowed.

That's all.


Imagine if it was, hipotetically you would have been allowed also to do this:

template<class T, int>
struct A { void f(); };

template<typename T>
void A<T, 1>::f() {}

template<>
struct A<int, 1> {};

That is, define f for a class template a specialization of which could not even have a declaration for f.

It doesn't make much sense, does it?

On the other side, consider the following member specialization:

template<>
void A<int, 1>::f() {}

It causes an instantiation of A<int, 1> that cannot be further specialized.

In other terms, you cannot do this in this case:

template<>
struct A<int, 1> {};

The presence of f is somehow guaranteed thus.

Partial template specialization with non-type parameters: GCC vs MSVS

The specific section of the standard dealing with the viability of a partial class template specialization has been changed lots of times over the last few years. The original restrictionin [temp.class.spec.match] read:

A partially specialized non-type argument expression shall not involve a template parameter of the partial specialization except when the argument expression is a simple identifier.

Your code clearly runs afoul of that, std::tuple_size<T>::value is not an identifier.

It then changed, after cwg issue 1315, to read:

Each template-parameter shall appear at least once in the template-id outside a non-deduced context.

But we're okay there - T is used in a non-deduced context as the first template parameter. And after template auto, it now reads:

If the template arguments of a partial specialization cannot be deduced because of the structure of its
template-parameter-list and the template-id, the program is ill-formed.

But we're okay there too. It can be deduced, you have the right "structure" - your specialization is using a non-type template parameter in the same location as the primary, and they should match fine.


I think following the resolution of 1315 (which I think is post-C++14), the code should be well-formed, but both gcc and clang reject it. An unfortunate fix would be to use two type parameters instead:

template <class T, class I>
struct S;

template<typename T>
struct S<T, typename std::tuple_size<T>::type> {};

template <size_t I>
using size_ = std::integral_constant<size_t, I>;

int main() {
S<std::tuple<int>, size_<1>> s;
}

Both gcc and clang accept that one.

Why are dependent template types not deducible in partial specialization?

During template argument deduction everything on the left-hand side of the scope resolution operator :: in a template argument of the partial specialization is a non-deduced context, meaning that a template parameter appearing there will not be deduced from the corresponding argument of the specialization.

Further, if part of a qualified type name is non-deduced context, then all parameters used to specify the type are non-deduced.

So in your example neither D nor E are deduced from typename Outer<D>::template Inner<E> and since there is no other way of deducing them, deduction fails, meaning that the partial specialization is never viable.

Specialization of non-type template argument

Your code is near by the needed solution. The specialization simply needs a bit different syntax:

template <typename T> class MyClass {};

template < auto value >
struct Foo
{
void Check() { std::cout << "Default" << std::endl; }
};

template <typename T, MyClass<T> value>
struct Foo<value>
{
void Check() { std::cout << "Spezial" << std::endl; }
};

int main()
{
Foo<10> fi;
Foo<MyClass<int>{}> fm;
fi.Check();
fm.Check();
}

For gcc it needs trunk version. gcc 11 compiles but delivers wrong result!

See it working: Works on gcc trunk, clang trunk and msvc v19.30

Specialize template template parameter with a non-type template parameter

There is a bug reported on Clang bugzilla for this titled: Alias template produces seemingly bogus "template template argument has different template parameters" error

This bug can be resolved in Clang 4 with the compiler option -frelaxed-template-template-args.

See demo here.

This has been fixed in later versions of Clang (from 5.0.0 onwards).

Dependent Non-Type Template Parameters

How can I fix this (i.e., keep internal pseudo-explicit specializations in a templated Foo) on VC++ 2010?

You can make the enumeration type non-dependent by declaring it in a non-template base class (C++03 made nested classes dependent in #108 but that doesn't include enumeration, but even if, such code would still be legal).

struct FooBase { 
enum Flags {Bar, Baz, Bax};
};

template<class> class Foo : public FooBase {
template< ::FooBase::Flags, class = void > struct Internal;
// same other stuff ...
};

The "error class" link already gives a description of the intended cases where the error should be risen. The error thinks that all dependent types are forbidden, but in fact this is what the Standard says:

The type of a template parameter corresponding to a specialized non-type argument shall not be dependent on a parameter of the specialization.

So even if the name Flags would be somehow dependent, that wouldn't make it ill-formed as long as it doesn't depend on a parameter of the specialization like in the example of your "error class" link.



Related Topics



Leave a reply



Submit