6

Source

In the following cases, the injected-class-name is treated as a template-name of the class template itself:

  • it is followed by <
  • it is used as a template argument that corresponds to a template template parameter
  • it is the final identifier in the elaborated class specifier of a friend class template declaration.

So I tried to examine all 3 cases (additionally in context of base ambiguity though I think it shouldn't matter here).

The first case seems simple.

The question is - why don't commented out examples work? They don't on both GCC & Clang so I don't think it's an implementation issue

template <template <class> class> struct A;

template <class T> struct Base {};

template <class T> struct Derived: Base<int>, Base<char>
{
    // #1
    typename Derived::Base<double> d;
    
    // #2
    
    // using a = A<Base>;

    using a = A<Derived::template Base>;

    // #3

    template<class U1>
    friend struct Base;

    // template<class U>
    // friend struct Derived::template Base;
};

Are the rules above only for template itself, not for bases? If so, what are rules for bases, especially for the last 2 of the cases?

Community
  • 1
  • 1
ledonter
  • 1,269
  • 9
  • 27
  • I can get a similar repro of [#2](https://wandbox.org/permlink/HV1oXmrAAOSjcD7P) that is smaller. The problem also goes away [if you use ::Base](https://wandbox.org/permlink/FHB08VEipoZmqvn3), interesting. – Ryan Haining May 08 '18 at 20:21

1 Answers1

2

The relevant rule here is [temp.local]/4:

A lookup that finds an injected-class-name ([class.member.lookup]) can result in an ambiguity in certain cases (for example, if it is found in more than one base class). If all of the injected-class-names that are found refer to specializations of the same class template, and if the name is used as a template-name, the reference refers to the class template itself and not a specialization thereof, and is not ambiguous. [ Example:

template <class T> struct Base { };
template <class T> struct Derived: Base<int>, Base<char> {
  typename Derived::Base b;             // error: ambiguous
  typename Derived::Base<double> d;     // OK
};

— end example ]

Which I think means that this is a gcc and a clang bug. In:

using a = A<Base>;

All of the injected-class-names that are found do refer to specializations of the same class template (Base<T>) and the name is used as a template-name (because it's a template argument for a template template parameter), so this should just refer to the class template itself and not be ambiguous.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • So are you saying both of the commented out examples should compile? I'm just worried about error message: "'Base' is neither function nor member function; cannot be declared friend", doesn't say anything about ambiguity; I understand it doesn't have to, but still... – ledonter May 08 '18 at 20:17
  • @ledonter I believe so yeah. – Barry May 08 '18 at 20:35