3

I have carefully read many answers concerning this topic, but nevertheless I cannot figure out EXACTLY when these two keywords ARE or AREN'T needed in the scope of a non-template function which is member of a nested template class.

My reference compilers are GNU g++ 4.9.2 and clang 3.5.0.

They behave hardly different on the following code where I put embedded comments trying to explain what happens.

#include <iostream>

// a simple template class with a public member template struct
template <class Z>
class Pa
{
// anything
public:
    template <class U>
    struct Pe  // a nested template
    {
        // anything
        void f(const char *); // a non-template member function
    };

    template <class U> friend struct Pe;
};

// definition of the function f
template <class AAA>
template <class BBB>
void Pa<AAA> :: Pe<BBB> :: f(const char* c)
{
    Pa<AAA> p; // NO typename for both clang and GNU...

    // the following line is ACCEPTED by both clang and GNU
    // without both template and typename keywords
    // However removing comments from typename only
    // makes clang still accepting the code while GNU doesn't
    // accept it anymore. The same happens if the comments   of template
    // ONLY are removed.
    //  
    // Finally both compilers accept the line when both typename AND
    // template are present...
    /*typename*/ Pa<AAA>::/*template*/ Pe<BBB> q;

    // in the following clang ACCEPTS typename, GNU doesn't:
    /*typename*/ Pa<AAA>::Pe<int> qq;

    // the following are accepted by both compilers
    // no matter whether both typename AND template
    // keywords are present OR commented out:
    typename Pa<int>::template Pe<double> qqq;
    typename Pa<double>::template Pe<BBB>  qqqq;
    std::cout << c << std::endl; // just to do something...
}

int main()
{
    Pa<char>::Pe<int> pp;
    pp.f("bye");
}

So, in the scope of f is Pa<double>::Pe<BBB> a dependent name or not?

And what about Pa<AAA>::Pe<int> ?

And, after all, why this different behaviour of the two quoted compilers?

Can anyone clarify solving the puzzle?

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
GSi
  • 649
  • 3
  • 10
  • "They behave hardly different"... as I understand your question, you wonder why the do behave different, in this case "hardly" is the wrong word. Actually what you describe is that they hardly behave the same ;) – 463035818_is_not_an_ai Sep 03 '15 at 13:44
  • GCC can't seem to cope with the name anymore as soon as you sprinkle `typename` or `template` and "forgets" about `P` being the surrouding template. I think GCC has a bug here and recommend making a PR. There is no need for `typename` or `template` here. – Johannes Schaub - litb Sep 03 '15 at 13:57
  • 1
    The rules for *allowing* `typename` where it is strictly not needed have been relaxed recently. Probably after the release of gcc 4.9. Could explain example 3. – Bo Persson Sep 03 '15 at 14:01
  • 1
    `Pa::Pe` is a dependent name in the sense that its type is dependent on `BBB`. But that doesn't mean you are required to insert `typename`. This is true also for `Pa::Pe` and `Pa`. In all these cases, the compiler can figure out itself that `Pe` is a template and `Pe` / `Pe` is a type. See http://stackoverflow.com/a/17579889/34509 – Johannes Schaub - litb Sep 03 '15 at 14:04

1 Answers1

3

The important rule in [temp.res] is:

When a qualified-id is intended to refer to a type that is not a member of the current instantiation (14.6.2.1) and its nested-name-specifier refers to a dependent type, it shall be prefixed by the keyword typename, forming a typename-specifier. If the qualified-id in a typename-specifier does not denote a type, the program is ill-formed.

The question revoles around two qualified-ids:

Pa<double>::Pe<BBB>
Pa<AAA>::Pe<int>

First, what is a dependent type? According to [temp.dep.type]:

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 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
— denoted by decltype(expression), where expression is type-dependent (14.6.2.2).

Pa<double> (the nested-name-specifier of the first example) is not a dependent type, as it fits none of the bullet points. Since we don't meet that criteria, we don't need to prefix the typename keyword.

Pa<AAA>, however, is a dependent type since it is a simple-template-id in which one of the template arguments is a dependent type (AAA is trivially a dependent type since it is a template parameter).

What, then, is "a member of the current instantiation"?

A name refers to the current instantiation if it is
— [...]
— in the definition of a primary class template or a member of a primary class template, the name of the class template followed by the template argument list of the primary template (as described below) enclosed in <> (or an equivalent template alias specialization)" — in the definition of a nested class of a class template, the name of the nested class referenced as a member of the current instantiation, or

The current instantiation, in this case, is Pa<AAA> (or, also, Pa). And:

A name is a member of the current instantiation if it is [...] A qualified-id in which the nested-name-specifier refers to the current instantiation and that, when looked up, refers to at least one member of a class that is the current instantiation or a non-dependent base class thereof.

So Pe is a member of the current instantiation. Thus, while the nested-name-specifier of Pa<AAA>::Pe<int> is a dependent type, it is a type that is a member of the current instantiation, so you do not need the keyword typename. Note that Pa<AAA>::Pe<int> is a dependent type itself (it's a nested class that is a dependent member of the current instantiation), but that in itself does mean that the typename keyword is required.

The fact that gcc doesn't accept typename here:

/*typename*/ Pa<AAA>::Pe<int> qq;

because it wants

typename Pa<AAA>::template Pe<int> qq;

is a bug.

Barry
  • 286,269
  • 29
  • 621
  • 977