Question

I have some code that, for the purposes of this question, boils down to

template<typename T>
class TemplateClass : public T {
 public:
  void method() {}
  template<typename U>
  static void static_method(U u) { u.TemplateClass::method(); }
};

class EmptyClass {};

int main() {
  TemplateClass<TemplateClass<EmptyClass> > c;
  TemplateClass<EmptyClass>::static_method(c);
}

I've tried to compile it with several versions of two compilers. GCC 4.2, 4.4, 4.6 accept it without complaint. Clang 2.9 and SVN trunk as of November 14 reject it with the following error message:

example.cc:6:38: error: lookup of 'TemplateClass' in member access expression is
      ambiguous
  static void static_method(U u) { u.TemplateClass::method(); }
                                     ^
example.cc:13:3: note: in instantiation of function template specialization
      'TemplateClass<EmptyClass>::static_method<TemplateClass<TemplateClass<EmptyClass>
      > >' requested here
  TemplateClass<EmptyClass>::static_method(c);
  ^
example.cc:2:7: note: lookup in the object type
      'TemplateClass<TemplateClass<EmptyClass> >' refers here
class TemplateClass : public T {
      ^
example.cc:2:7: note: lookup from the current scope refers here
1 error generated.

Which one is wrong? I can work around Clang by changing

  static void static_method(U u) { u.TemplateClass::method(); }

to

  static void static_method(U u) { u.TemplateClass<T>::method(); }

but I'd like be confident in my understanding of when it's OK to elide the template parameters.


EDIT: I had thought that the ambiguity was between the two instantiations of TemplateClass. The following code compiles with GCC and Clang, calling that hypothesis into doubt:

class E {};

template<typename T>
class A : public T {
 public:
  void method() {}
};

int main() {
  A<A<E> > a;
  a.A::method();
}
Was it helpful?

Solution

I believe that clang is correctly rejecting this code.

The ambiguity that clang finds can be reproduced with a less complicated example:

template<typename T>
class TemplateClass {
 public:
  void method() {}
  template<typename U>
  static void static_method(U u) { u.TemplateClass::method(); }                                  
};

struct A {};
struct B {};

int main() {
  TemplateClass<A> c;
  TemplateClass<B>::static_method(c);
}

Here the inheritance in the template is omitted and two independent classes are used for the instantiations. The error produced by clang remains the same.

First of all, in the scope of TemplateClass<T> the name TemplateClass refers to TemplateClass<T>, due to class name injection. This is the reason that the static method can use TemplateClass::method instead of a more explicit TemplateClass<T>::method.

The name lookup used to interpret u.TemplateClass::method in the static method is defined in "3.4.5 Class member access [base.lookup.classref]" of the C++11 and C++98 standards.

The relevant part is 3.4.5/4:

If the id-expression in a class member access is a qualified-id of the form

class-name-or-namespace-name::...

[...]

This is the case here. The id-expression is the part to the right of the . and in our case this is the qualified name TemplateClass::method.

[...]
the class-name-or-namespace-name following the . or -> operator is looked up both in the context of the entire postfix-expression and in the scope of the class of the object expression.

The "scope of the entire postfix-expression" is the body of the static function, and in this static function TemplateClass refers to TemplateClass<B>, since the function is a member of that class (we called TemplateClass<B>::static_method).

So in this scope the name refers to TemplateClass<B>.

The "object expression" is the part left of ., in our case c. The class of c is TemplateClass<A> and in the scope of this class, TemplateClass refers to TemplateClass<A>.

So, depending on the scope used for the lookup, the name refers to a different entity.

The standard now says:

If the name is found in both contexts, the class-name-or-namespace-name shall refer to the same entity.

This is not the case in our program. The program is ill-formed, and the compiler is required to give a diagnostic message.

The ambiguity stays the same if you replace B with TemplateClass<A>, as used in the question.

OTHER TIPS

In ISO/IEC 14882:2011(E), "14.6.1 Locally declared names [temp.local]", [#5] says:

When the normal name of the template (i.e., the name from the enclosing scope, not the injected-class-name) is used, it always refers to the class template itself and not a specialization of the template.[ Example:

template<class T> class X {
    X* p;    // meaning X<T>
    X<T>* p2;
    X<int>* p3;
    ::X* p4;    // error: missing template argument list
                // ::X does not refer to the injected-class-name
};
— end example ]

This leads me to believe that in your example u.TemplateClass::method(); is equivalent to u.TemplateClass<T>::method(); and if Clang gives an error in one case and compiles cleanly in the other case, then it's a Clang error.

When we call these two lines:

TemplateClass<TemplateClass<EmptyClass> > c;
TemplateClass<std::string>::static_method(c);

then the type argument U is the type of the object c:

TemplateClass<TemplateClass<EmptyClass> >

Let's leave static_method, and do an experiment:

#include <iostream>
#include <typeinfo.h>

using namespace std;

template<typename T>
class TemplateClass : public T {
public:
  void method(int i) {
    cout << i << ": ";
    cout << typeid(*this).name() << endl; 
  }
};

class EmptyClass { };

void main() {
  TemplateClass<TemplateClass<EmptyClass> > u;
  u.method(1);
  u.TemplateClass::method(2);
  u.TemplateClass<EmptyClass>::method(3);
  u.TemplateClass<TemplateClass<EmptyClass> >::method(4);
}

The output is:

1: class TemplateClass<class TemplateClass<class EmptyClass> >
2: class TemplateClass<class TemplateClass<class EmptyClass> >
3: class TemplateClass<class EmptyClass>
4: class TemplateClass<class TemplateClass<class EmptyClass> >

In all four cases (and inside static_method) we call TemplateClass<T>::method, and the type name given between u. and :: will give the actual type T:

  • Case #1 is the default, here T is given by the declaration of u.
  • Case #4 is also trivial.
  • Case #2 looks as if the compiler should have guessed the type argument of TemplateClass, which is trivially the one given in the declaration of u.
  • Case #3 is very interesting. I guess function type casting happened here, from TemplateClass<TemplateClass<EmptyClass> >::method to TemplateClass<EmptyClass>::method.

I don't know whether this behavior is part of the C++ standard.

EDIT:

Actually case #3 is not casting, these are qualified names. So in conclusion, Clang is not aware of this qualification syntax, while both GCC and Visual C++ 2010 are.

Not an answer,

just my small contribution:

Removing templates, but keeping the same names:

struct A {
    struct TemplateClass {
        void method() {}
    };
};
struct B {
    struct TemplateClass {
        void method() {}

        static void static_method(A::TemplateClass u) { 
            u.TemplateClass::method(); 
        }
    };
};

int main() {
    A::TemplateClass c;
    B::TemplateClass::static_method(c);
}

gives

Comeau C/C++ 4.3.10.1 (Oct  6 2008 11:28:09) for ONLINE_EVALUATION_BETA2
Copyright 1988-2008 Comeau Computing.  All rights reserved.
MODE:strict errors C++ C++0x_extensions

"ComeauTest.c", line 11: error: ambiguous class member reference -- type
          "B::TemplateClass::TemplateClass" (declared at line 7) used in
          preference to type "A::TemplateClass::TemplateClass" (declared at
          line 2)
            u.TemplateClass::method(); 
              ^

"ComeauTest.c", line 11: error: qualified name is not a member of class
          "A::TemplateClass" or its base classes
            u.TemplateClass::method(); 
              ^

2 errors detected in the compilation of "ComeauTest.c".

From N3242

Locally declared names [temp.local]

Like normal (non-template) classes, class templates have an injected-class-name (Clause 9). The injected class-name can be used with or without a template-argument-list. When it is used without a template-argument-list, it is equivalent to the injected-class-name followed by the template-parameters of the class template enclosed in <>.

(...)

Within the scope of a class template specialization or partial specialization, when the injected-class-name is not followed by a <, it is equivalent to the injected-class-name followed by the template-arguments of the class template specialization or partial specialization enclosed in <>.

(...)

A lookup that finds an injected-class-name (10.2) can result in an ambiguity in certain cases

Having never used Clang, I was quite interested in this problem. (Ironic, yes I know.)

Clang C++ Compatibility indicates that there are several things regarding templates that other compilers (notably GCC) process that it will complain about. These are things that are weakly defined in the standard ("well, you shouldn't allow this ... but you can"); nearly all of them involve templates. None of these exactly look like your problem, but they're close enough to be informative -- and certainly worth a read.

So, it doesn't look like Clang is broken -- it's just that Clang is pickier than the others.

I think the ambiguity is because TemplateClass is twice in the inheritance TemplateClass : (TemplateClass : EmptyClass)

Does u.TemplateClass::method(); mean u.TemplateClass<TemplateClass<EmptyClass> >::method(); or u.TemplateClass<EmptyClass> >::method(); ?

Perhaps GCC has the standard right, but whatever the case is you should add the <T>.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top