Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template disambiguator for dependent names

Tags:

c++

I encountered the following problem by compiling the following example:

template <int N>
class Matrix {
public:
    template <int Idx>
    int head() {
        return Idx;
    }
};

template <typename T>
class Test {
    static constexpr int RayDim = 3;
public:
    int func() const {
        Matrix<RayDim> yF;
        return yF.head<1>();
        //        ^ is template keyword required here?
    }
};

struct Empty {};

void test() {
    Test<Empty> t;
}

Link to Compiler Explorer: https://godbolt.org/z/js4XaP

The code compiles with GCC 9.2 and MSVC 19.22 but not with clang 9.0.0. Clang states that a template keyword is required. If static constexpr int RayDim = 3; is moved into int func() const clang accepts it.

As stated as a comment in the code block, is the template keyword required for yF.head<1>()?

like image 213
Jodebo Avatar asked Nov 13 '19 20:11

Jodebo


People also ask

What is dependent name in C++?

A dependent name is essentially a name that depends on a template parameter. A dependent name can be a type, a non-type, or a template parameter. To express that a dependent name stands for a type or a template, you have to use the keywords typename or template .

What is a dependent name?

A dependent name is a name that depends on the type or the value of a template parameter. For example: template<class T> class U : A<T> { typename T::B x; void f(A<T>& y) { *y++; } }; The dependent names in this example are the base class A<T> , the type name T::B , and the variable y .

Does C++ have dependent types?

Technically (and truly), C++ has dependent types, as types can depend on values ... | Hacker News.

What is type name in template?

" typename " is a keyword in the C++ programming language used when writing templates. It is used for specifying that a dependent name in a template definition or declaration is a type.


1 Answers

The template keyword should not be required here, so clang is incorrect to reject the program.

All C++ Standard section and paragraph numbers and quotes below are the same for C++17 draft N4659 and the current linked C++20 draft.

The requirement for template after a . or -> or :: token when naming a member template is in [temp.names]/4. The paragraph first lists cases where the keyword is not allowed, then cases where it is optional and doesn't make a difference, then:

In all other contexts, when naming a template specialization of a member of an unknown specialization ([temp.dep.type]), the member template name shall be prefixed by the keyword template.

A "member of an unknown specialization" is a member of a dependent type which is not "the current instantiation". So the question is whether Matrix<RayDim> is a dependent type. For that, we look at [temp.dep.type]/9:

A type is dependent if it is

  • a template parameter,
  • a member of an unknown specialization,
  • a nested class or enumeration that is a dependent member of the current instantiation,
  • a cv-qualified type where the cv-unqualified type is dependent,
  • a compound type constructed from any dependent type,
  • an array type whose element type is dependent or whose bound (if any) is value-dependent,
  • a function type whose exception specification is value-dependent,
  • a simple-template-id in which either the template name is a template parameter or any of the template arguments is a dependent type or an expression that is type-dependent or value-dependent or is a pack expansion, or
  • denoted by decltype(expression), where expression is type-dependent.

Matrix<RayDim> is clearly not a template parameter, any kind of member, cv-qualified, an array type, a function type, or specified by decltype. It is a compound type, but it uses only a template-name and an expression, so is not constructed from any other type.

That leaves the simple-template-id case. The template name Matrix is not a template parameter. The template argument RayDim is an expression, so now to see if it is type-dependent or value-dependent.

"Type-dependent" is defined in [temp.dep.expr]. Only paragraph 3 can apply to a lone identifier like RayDim:

An id-expression is type-dependent if it contains

  • an identifier associated by name lookup with one or more declarations declared with a dependent type,
  • an identifier associated by name lookup with a non-type template-parameter declared with a type that contains a placeholder type,
  • an identifier associated by name lookup with a variable declared with a type that contains a placeholder type ([dcl.spec.auto]) where the initializer is type-dependent,
  • an identifier associated by name lookup with one or more declarations of member functions of the current instantiation declared with a return type that contains a placeholder type,
  • an identifier associated by name lookup with a structured binding declaration whose brace-or-equal-initializer is type-dependent,
  • the identifier __func__ ([dcl.fct.def.general]), where any enclosing function is a template, a member of a class template, or a generic lambda,
  • a template-id that is dependent,
  • a conversion-function-id that specifies a dependent type, or
  • a nested-name-specifier or a qualified-id that names a member of an unknown specialization;

or if it names a dependent member of the current instantiation that is a static data member of type "array of unknown bound of T" for some T ([temp.static]).

RayDim certainly does not contain any __func__, template-id, conversion-function-id, nested-name-specifier, or qualified-id. Name lookup finds the class template's static member declaration. That declaration of RayDim is certainly not a template-parameter, a member function, or a structured binding declaration, and its type const int is certainly not a dependent type or array type and does not contain a placeholder type. So RayDim is not type-dependent.

"Value-dependent" is defined in [temp.dep.constexpr]. The only cases that can apply to a lone identifier like RayDim are in paragraph 2:

An id-expression is value-dependent if:

  • it is type-dependent,
  • it is the name of a non-type template parameter,
  • it names a static data member that is a dependent member of the current instantiation and is not initialized in a member-declarator,
  • it names a static member function that is a dependent member of the current instantiation, or
  • it is a constant with literal type and is initialized with an expression that is value-dependent.

From above, RayDim is not type-dependent. It is certainly not a template parameter or a member function. It is a static data member and dependent member of the current instantiation, but it is initialized in the member-declarator. That is, the "= 3" appears inside the class definition, not in a separate member definition. It is a constant with literal type, but its initializer 3 is not value-dependent.

So RayDim is not value-dependent or type-dependent. Therefore Matrix<RayDim> is not a dependent type, yF.head is not a member of an unknown instantiation, and the template keyword before head is optional, not required. (It is permitted since it's not in a "type-only context" and head does in fact name a member template.)

like image 170
aschepler Avatar answered Nov 10 '22 00:11

aschepler