xmh0511
xmh0511

Reputation: 7369

Are both the keyword "typename" and "template" not necessary in this case?

template<class T> 
T::type<int> f(){
    
}

According to [temp.names#3.4]

A < is interpreted as the delimiter of a template-argument-list if it follows a name that is not a conversion-function-id and

  • [...]
  • that is a terminal name in a using-declarator ([namespace.udecl]), in a declarator-id ([dcl.meaning]), or in a type-only context other than a nested-name-specifier ([temp.res]).

According to [temp.res#general-4.3.1], T::type<int> does satisfy the above rule(emphasized mine) due to the following rule

A qualified or unqualified name is said to be in a type-only context if it is the terminal name of

  • [...]
  • a decl-specifier of the decl-specifier-seq of a
  • [...]
  • simple-declaration or a function-definition in namespace scope,

T::type<int> is the decl-specifier of the function-definition for template function f that is in the namespace scope, hence the terminal name type is said to be in the type-only context.

Also, according to [temp.res#general-5]

A qualified-id whose terminal name is dependent and that is in a type-only context is considered to denote a type.

Hence, the symbol < in T::type<int> is interpreted as the delimiter of the template-argument-list due to [temp.names#3.4] while the qualified-id T::type<int> is considered to denote a type due to [temp.res#general-5], the example should be legal. However, it has been rejected by both Clang and GCC.

I wonder, Are both the keyword typename and template not necessary in this example compiled by future implementations?

Upvotes: 8

Views: 105

Answers (1)

Davis Herring
Davis Herring

Reputation: 40053

Yes, this is the rule, and it’s correct; compilers simply haven’t implemented the (newer) template part yet. In discussing that addition, an example was brought up that illustrates the absurdity of requiring the keyword in this context:

template<typename T> struct A {
  template<typename U> struct B {
    B();
  }; 
  template<typename U> B<U> make();
};
template<typename T> template<typename U>
A<T>::B<U>::B() {} // no 'template' keyword required before 'B' here, but...
template<typename T> template<typename U>
A<T>::B<U> A<T>::make() { return {}; } // 'template' keyword required before 'B' here?

This also illustrates part of the motivation for dropping the requirement for typename in many contexts. A<T>::B might be a dependent name (if the declaration ends up being for something that’s not a member of (the primary template of) A), but that doesn’t interfere with parsing it since no expression can appear there.

Upvotes: 4

Related Questions