In the answer to this post "(Partially) specializing a non-type template parameter of dependent type", it states:
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 ]
My question is why is this restriction here? There is at least one use case where I find that this restriction interferes with writing clean code. E.g.
template <typename T, T*> struct test; template <typename T> struct test<T, nullptr> // or struct test<T, (T*)nullptr> { }; template <typename R, typename...ARGs, R(*fn)(ARGs...)> struct test<R(ARGs...), fn> { };
Though I'm unsure if there are other cases that stating a constant based on a type is a problem beyond not making any sense.
Anyone have a reason for why this is so?
Which parameter is legal for non-type template? Explanation: The following are legal for non-type template parameters:integral or enumeration type, Pointer to object or pointer to function, Reference to object or reference to function, Pointer to member.
Just like in case of the function arguments, template parameters can have their default values. All template parameters with a default value have to be declared at the end of the template parameter list.
A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)
In C++ this can be achieved using template parameters. A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.
(IMHO) The most common reasons the standard disallows a specific feature are:
Difficulty of implementation is rarely a factor, though it may take some time for compiler implementations to catch up with evolution on the "hard" stuff.
You could always wrap your non type template parameter in another type:
template < typename T1, typename T2 > struct Demo {}; // primary template template < typename T > struct Demo<T, integral_constant<T, 0>> {}; // specialization
I doubt this hack falls into case 1. Case 3 is always a possibility so lets examine case 2. To do this, we have to know which are the related rules the standard imposes on class templates partial specializations.
14.5.5 Class template partial specializations
A non-type argument is non-specialized if it is the name of a non-type parameter. All other non-type arguments are specialized. (C1)
Within the argument list of a class template partial specialization, the following restrictions apply:
- 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. (C2)
- The type of a template parameter corresponding to a specialized non-type argument shall not be dependent on a parameter of the specialization. (C3)
I marked the first three Clauses I found relevant (the third is the one in question). According to C1 in our case we have a specialized non-type argument so C2 should stand, yet this
template <class T, T t> struct C {}; template <class T> struct C<T, 1>;
is actually
template <class T, T t> struct C {}; template <class T> struct C<T, T(1)>; // notice the value initialization
so the partially specialized non type argument T t
involves the template parameter of the partial specialization class T
in an expression other than an identifier; furthermore such specializations are bound to involve class T
in a value initialization which will always be a violation of the rules. Then C3 comes along and clears that out for us so that we won't have to make that deduction every time.
So far we've established that the rules are in sync with themselves but this does NOT prove case 2 (once we remove the initial limitation every other related limitation falls apart). We'd have to dive into matching class template partial specializations rules; partial ordering is considered out of scope here because if we can produce valid candidates it's up to the programmer to put together a well formed program (i.e. not create ambiguous uses of class templates).
Section
Matching of class template partial specializations [temp.class.spec.match]
describes the (give or take) "pattern matching" process involved in template specialization. Rule 1
is the overall workflow of the procedure and the subsequent rules are those that define correctness
A partial specialization matches a given actual template argument list if the template arguments of the partial specialization can be deduced from the actual template argument list
A non-type template argument can also be deduced from the value of an actual template argument of a non-type parameter of the primary template.
In a type name that refers to a class template specialization, (e.g., A) the argument list shall match the template parameter list of the primary template. The template arguments of a specialization are deduced from the arguments of the primary template.
These rules are not violated by allowing the type of a template parameter corresponding to a specialized non-type argument to be dependent on a parameter of the specialization. So IMHO there is no specific reason why we can't have this feature in future revisions of the language: legacy is to blame. Sadly I didn't manage to find any language proposals with the initiative to introduce this feature.
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