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?
K
, which is the injected class name, has a dual nature in the scope of K<int>
. You can use it without template arguments. Then it refers to K<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 template
because 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, K
is 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:
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
};
One might be inclined to conclude from this that the intent is that you could leave off the template
. The Standard says
For a template-name to be explicitly qualified by the template arguments, the name must be known to refer to a template.
I can't see how this wouldn't apply to T::K<T>
. If T
is a dependent type then you can just lean back because you can't know what K
refers to when parsing it, so to make any sense of the code, you just have to be able to prefix it with template
. Notice that n3225 has that example too, but it's not a defect there: You can officially leave off template
if 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.
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 name K<int>::K
will refer to the constructor(s) of K<int>
unless the name lookup used will ignore function (constructor) names. Will typename T::K
ignore function names? 3.4.4/3 says about elaborated type specifiers, which typename ...
is one of:
If the name is a qualified-id, the name is looked up according its qualifications, as described in 3.4.3, but ignoring any non-type names that have been declared.
However, a typename ...
uses different lookup. 14.6/4 says
The usual qualified name lookup (3.4.3) is used to find the qualified-id even in the presence of typename.
The 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):
If the nested-name-specifier nominates a class C, and the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name of C (clause 9), the name is instead considered to name the constructor of class C. Such a constructor name shall be used only in the declarator-id of a constructor definition that appears outside of the class definition.
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:
// just replace struct X with this:
template<typename T>
struct X
{
struct Derived : T { };
typename Derived::template K<T> *p;
};
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 use T::K<int>
"ComeauTest.c", line 13: error: overloaded function "N::K<T>::K [with T=int]" is
not a template
typename T::template K<T> *p;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With