Title
Unclear definition of “equivalent to a nontype template parameter”
Status
cd5
Section
13.8.3.2 [temp.dep.type]
Submitter
Richard Smith

Created on 2016-07-21.00:00:00 last changed 2 months ago

Messages

Date: 2017-11-15.00:00:00

Notes from the November, 2017 (Albuquerque) meeting:

CWG decided to proceed with this resolution and deal with partial specialization in a separate issue.

Date: 2017-11-15.00:00:00

Additional note (November, 2017):

It was observed that the proposed resolution does not address partial specializations, which also depend on the definition of equivalence. For example:

  template <typename T, unsigned N> struct A; 
  template <typename T> struct A<T, 42u> { 
    typedef int Ty; 
    static const unsigned num = 42u; 
    static_assert(!A<T, num>::Ty(), ""); 
  }; 
  A<int, 42u> a; // GCC, MSVC, ICC accepts; Clang rejects 

The issue is being returned to "review" status in order to consider these additional questions.

Date: 2017-08-15.00:00:00

Proposed resolution (August, 2017):

Change 13.8.3.2 [temp.dep.type] paragraph 3 as follows:

A template argument that is equivalent to a template parameter (i.e., has the same constant value or the same type as the template parameter) can be used in place of that template parameter in a reference to the current instantiation. For a template type-parameter, a template argument is equivalent to a template parameter if it denotes the same type. For a non-type template parameter, a template argument is equivalent to a template parameter if it is an identifier that names a variable that is equivalent to the template parameter. A variable is equivalent to a template parameter if

  • it has the same type as the template parameter (ignoring cv-qualification) and

  • its initializer consists of a single identifier that names the template parameter or, recursively, such a variable.

[Note: Using a parenthesized variable name breaks the equivalence. —end note] In the case of a non-type template argument, the argument must have been given the value of the template parameter and not an expression in which the template parameter appears as a subexpression. [Example:

  template <class T> class A {
    A* p1;                   // A is the current instantiation
    A<T>* p2;                // A<T> is the current instantiation
    A<T*> p3;                // A<T*> is not the current instantiation
    ::A<T>* p4;              // ::A<T> is the current instantiation
    class B {
      B* p1;                 // B is the current instantiation
      A<T>::B* p2;           // A<T>::B is the current instantiation
      typename A<T*>::B* p3; // A<T*>::B is not the current instantiation
    };
  };

  template <class T> class A<T*> {
    A<T*>* p1;               // A<T*> is the current instantiation
    A<T>* p2;                // A<T> is not the current instantiation
  };

  template <class T1, class T2, int I> struct B {
    B<T1, T2, I>* b1;        // refers to the current instantiation
    B<T2, T1, I>* b2;        // not the current instantiation
    typedef T1 my_T1;
    static const int my_I = I;
    static const int my_I2 = I+0;
    static const int my_I3 = my_I;
    static const long my_I4 = I;
    static const int my_I5 = (I);
    B<my_T1, T2, my_I>* b3;   // refers to the current instantiation
    B<my_T1, T2, my_I2>* b4;  // not the current instantiation
    B<my_T1, T2, my_I3>* b5;  // refers to the current instantiation
    B<my_T1, T2, my_I4>* b6;  // not the current instantiation
    B<my_T1, T2, my_I5>* b7;  // not the current instantiation
  };

end example]

Date: 2017-11-15.00:00:00

[Accepted as a DR at the November, 2017 meeting.]

The description of whether a template argument is equivalent to a template parameter in 13.8.3.2 [temp.dep.type] paragraph 3 is unclear as it applies to non-type template parameters:

A template argument that is equivalent to a template parameter (i.e., has the same constant value or the same type as the template parameter) can be used in place of that template parameter in a reference to the current instantiation. In the case of a non-type template argument, the argument must have been given the value of the template parameter and not an expression in which the template parameter appears as a subexpression.

For example:

  template<int N> struct A { 
    typedef int T[N]; 
    static const int AlsoN = N; 
    A<AlsoN>::T s; // #0, clearly supposed to be OK 
    static const char K = N; 
    A<K>::T t;     // #1, OK? 
    static const long L = N; 
    A<L>::T u;     // #2, OK? 
    A<(N)>::T v;   // #3, OK? 
    static const int M = (N); 
    A<M>::T w;     // #4, OK? 
  }; 

#1 narrows the template argument. This obviously should not be the injected-class-name, because A<257>::T may well be int[1] not int[257] . However, the wording above seems to treat it as the injected-class-name.

#2 is questionable: there is potentially a narrowing conversion here, but it doesn't actually narrow any values for the original template parameter.

#3 is hard to decipher. On the one hand, this is an expression involving the template parameter. On the other hand, a parenthesized expression is specified as being equivalent to its contained expression.

#4 should presumably go the same way that #3 does.

History
Date User Action Args
2020-12-15 00:00:00adminsetstatus: dr -> cd5
2018-02-27 00:00:00adminsetmessages: + msg6123
2018-02-27 00:00:00adminsetmessages: + msg6122
2018-02-27 00:00:00adminsetmessages: + msg6121
2016-07-21 00:00:00admincreate