Why is initialization of a constant dependent type in a template parameter list disallowed by the standard?

。_饼干妹妹 提交于 2019-12-30 00:49:05

问题


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?


回答1:


(IMHO) The most common reasons the standard disallows a specific feature are:

  1. The feature is covered by another mechanism in the language, rendering it superfluous.
  2. It contradicts existing language logic and implementation, making its implementation potentially code breaking.
  3. Legacy: the feature was left out in the first place and now we've built a lot without it that it's almost forgotten (see partial function template specialization).

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

  1. 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)

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

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

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

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



来源:https://stackoverflow.com/questions/32104890/why-is-initialization-of-a-constant-dependent-type-in-a-template-parameter-list

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!