Check out the following code (written just for fun)
namespace N
{
template<typename T>
struct K
{
};
}
template<typename T>
struct X
{
typename T::template K<T> *p; //should give error
//N::K<int> has no template member named `K`
};
int main()
{
X<N::K<int> > l;
}
The code gets compiled on g++(4.5.1) and Clang whereas Comeau and Intel C++ give (similar) errors.
The errors that I get on Comeau are :
"ComeauTest.c", line 13: error: class "N::K<int>" has no member "K"
typename T::template K<T> *p;
^
detected during instantiation of class "X<T> [with T=N::K<int>]" at
line 18
"ComeauTest.c", line 13: error: expected an identifier
typename T::template K<T> *p;
^
detected during instantiation of class "X<T> [with T=N::K<int>]" at
line 18
So my question is “Is the code sample ill-formed ?” According to me “Yes”. Does that mean this is yet another bug in g++/Clang?
Why GCC and Clang think they are right
K, which is the injected class name, has a dual nature in the scope ofK<int>. You can use it without template arguments. Then it refers toK<int>(to its own type).It can be followed by a template argument list too. IMO it’s reasonable to say that you need to prefix it with
templatebecause of the parser ambiguity with the<that follows. It then refers to the specified type that’s determined by the template arguments.So it can be treated as a member template and as a nested type, depending on whether it’s followed by a template argument list. Of course,
Kis not really a member template. The dual nature of the injected class name seems to me more of a hack anyway, though.The Standard has an example that reads like this:
One might be inclined to conclude from this that the intent is that you could leave off the
template. The Standard saysI can’t see how this wouldn’t apply to
T::K<T>. IfTis a dependent type then you can just lean back because you can’t know whatKrefers to when parsing it, so to make any sense of the code, you just have to be able to prefix it withtemplate. Notice that n3225 has that example too, but it’s not a defect there: You can officially leave offtemplateif you lookup into the template’s own scope in C++0x (it’s called the “current instantiation”).So until now, Clang and GCC are fine.
Why Comeau is right
Just to make it even more complicated, we will have to consider the constructors of
K<int>. There is a default constructor and a copy constructor implicitly declared. A nameK<int>::Kwill refer to the constructor(s) ofK<int>unless the name lookup used will ignore function (constructor) names. Willtypename T::Kignore function names? 3.4.4/3 says about elaborated type specifiers, whichtypename ...is one of:However, a
typename ...uses different lookup. 14.6/4 saysThe usual qualified lookup of 3.4.3 won’t ignore non-type names, as illustrated by the example attached to 14.6/4. So, we will find the constructor(s) as specified by 3.4.3.1/1a (the additional twist that this only happens when non-types are not ignored was added by a later defect report, which all popular C++03 compilers implement though):
So in the end, I think comeau is correct to diagnose this, because you try to put a template argument list onto a non-template and also violate the requirement quoted in the last part (you use the name elsewhere).
Let’s change it by accessing the injected name by a derived class, so no constructor name translation occurs, and you really access the type so that you really can append the template arguments:
Everything compiles now with comeau too! Notice I already did problem report to clang about this exact thing. See Incorrect constructor name resolution. BTW, if you declare a default constructor in
K, you can see comeau give a better error message if you useT::K<int>