C++ template parameter/class ambiguity - c++

while testing with different version of g++, the following problem came up
template<class bra>
struct Transform<bra, void> : kernel::Eri::Transform::bra {
static const size_t ni = bra::A::size;
bra::A is interpreted as kernel::Eri::Transform::bra::A, rather than template argument by g++ 4.1.2. on the other hand, g++ 4.3 gets it right.
what should be correct behavior according to standard?
Meanwhile, I refactor slightly to make problem go away.

Seems to me like gcc 4.1.2 was right. §14.6.1/7 (ISO/IEC 14882, C++03):
In the definition of a class template or in the definition of a member of such a template that appears outside of the template definition, for each base class which does not depend on a template-parameter (14.6.2), if the name of the base class or the name of a member of the base class is the same as the name of a template- parameter, the base class name or member name hides the template-parameter name (3.3.7).

Related

C++ template definition fails on clang, works on GCC

The following code compiles on GCC (doesn't even require a recent version), but it fails on clang 3.4:
template <int N>
struct T_unsigned {
typedef typename T_unsigned<N>::Type Type;
};
template <> struct T_unsigned<8> {
typedef unsigned char Type;
};
Using the above definition of T_unsigned, GCC allows you to use "T_unsigned<8>::Type" instead of "unsigned char". When I try to compile this using clang 3.4, I get:
test.cpp:3:41: error: no type named 'Type' in 'T_unsigned<N>'
typedef typename T_unsigned<N>::Type Type;
~~~~~~~~~~~~~~~~~~~~~~~~^~~~
1 error generated.
Is clang failing to compile correct C++11 code, or is this code doing something non-standard that GCC happens to support?
In your general case T_unsigned<N>, we have:
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.
[ Example:
template <class T> class A {
static const int i = 5;
int n1[i]; // i refers to a member of the current instantiation
int n2[A::i]; // A::i refers to a member of the current instantiation
int n3[A<T>::i]; // A<T>::i refers to a member of the current instantiation
int f();
};
Within T_unsigned<N>, T_unsigned<N> is just another name for itself. So basically you have something like:
class Foo {
typedef Foo::Type Type;
};
and the "correct" error message should be approximately (http://ideone.com/FvJHBF):
prog.cpp:2:17: error: ‘Type’ in ‘class Foo’ does not name a type
typedef Foo::Type Type;
^
However, you write that you have problems with using your specialization T_unsigned<8>, which is seemingly not found by clang.
Your testcase is not too exhaustive, so my answer depends on an if-statement:
If at the point of instantiation, your specialization for N=8 is visible, than clang is doubly wrong. If not, gcc and clang should both fail, but with aforementioned error message (though the error message itself is in no way defined by the standard, so this is a tool's should, not a standard's).
The name that you use as the typedef is not dependent (see the clauses that define dependent names) and the namelookup in definition context at that place will not find a declaration. That in itself is an error already.
But since there is no declaration for the name yet, the name is also not a member of a class that is the current instantiation or a base class thereof. Therefore the name is not a member of the current instantiation and therefore we have another reason to reject it by a rule that says that if the qualifier is the current instantiation, the name either must refer to a member of that instantiation, or to a member of an unknown specialization (which would be the case if the class had dependent base classes).
Note the notion of "current instantiation": the meaning of the qualifier is fixed to refer to the result of instantiating the surrounding template, we don't wait for resolving the template arguments. Hence the term is not called "current specialization", since we know that it refers to an instantiated specialization, as opposed to a later declared explicit specialization.
The thing is different for C++03 I think. The name will be dependent and the template definition is harder to deem illformed with the rules available. The illformed, no diagnostic required, behavior will however happen when you try to instantiate the template before providing the explicit specialization. I think such code refering to itself makes no sense because you never are able to actually instantiate the template validly (and there is a rule that allows rejecting a template straight away in such cases).

Different behavior of compilers - template template argument

Suppose i have the following code:
template <template <typename> class T>
class A {};
template <typename T>
class B
{
A<B> instance;
};
int main()
{
B<int> instance;
}
gcc 4.7.2 and gcc 4.8.0 compiles this code ok, while icc 13.0.1 and clang 3.2 gave me an error (clang require ::B instead of B, while icc also require whitespace after < in template instantiaion).
Who's right?
I found the thread about it (Template class that refers to itself as a template template parameter?), but i can't understand 14.6.1/2 of the standard and also saw LLVM bug 14350 (http://www.mail-archive.com/llvmbugs#cs.uiuc.edu/msg21095.html). So, clang and intel wrong here?
14.6.1 says:
The injected-class-name can be used as a template-name or a type-name. When it is used with a template-argument-list, as a template-argument for a template template-parameter, or as the final identifier in the elaborated-type-specifier of a friend class template declaration, it refers to the class template itself.
The "injected-class-name" is the name of the class template (B) "injected" into the scope of the class. In other words, it refers to the use of the unqualified name B within the definition of the class B. If you use that name in a context where a template name is required: i.e., with explicit template arguments (B<int>) or as a template argument for a template which takes a template template parameter (A<B>), it should refer to the template itself.
So, gcc is right.
Also, in C++11, you should not need a space after the < in <::B>. According to section 2.5, paragraph 3, when dividing the input stream into tokens:
if the next three characters are <:: and the subsequent character is neither : nor >, the < is treated as a preprocessor token by itself and not as the first character of the alternative token <:. (<: is another way of writing [.)

clang++ - treat template class name as template in the class scope

It seems that clang++ (I tried clang 3.2) treats the name of a template class as a instantiated class, not a template for any occurence within the class scope. For example, the following codes
template <template <class> class T>
class A {};
template <typename T>
class B {
A<B> member;
// ^---- clang++ treats B as an instantiated class
// but I want it to be a template here
// this code could compile in g++
};
int main()
{
B<int> b;
return 0;
}
What should I do to compile that?
C++03
Resolving B that way (called the injected-class-name, an implicitly-declared member of every class including template instantiations) is intended as a convenience. I've never seen it get in the way like that!
To work around, qualify the name by adding :: before it (and if necessary, the name of the namespace).
template <typename T>
class B {
A< ::B> member; // whitespace required to prevent digraph; see comments
};
C++11
C++11 §14.6.1/1 specifies (emphasis mine)
Like normal (non-template) classes, class templates have an injected-class-name (Clause 9). The injected- class-name can be used as a template-name or a type-name. When it is used with a template-argument-list, as a template-argument for a template template-parameter, or as the final identifier in the elaborated-type- specifier of a friend class template declaration, it refers to the class template itself. Otherwise, it is equivalent to the template-name followed by the template-parameters of the class template enclosed in <>.
Therefore, if this problem occurs under C++11 it is a compiler bug. Workaround as above.
Note — for comparison, the corresponding paragraph in C++03 is
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 <>. When it is used with a template-argument-list, it refers to the specified class template specialization, which could be the current specialization or another specialization.
As you can see, there's already one special case allowing the identifier to be a class or template, depending on whether it occurs in a template-name. They just added a couple more cases.
This is nonconformant behavior as of C++11 because C++11 says that the injected class name (which is a name automatically declared within the class body) is a template when it is passed to a template template parameter. So your code should only fail in a C++03 implementation.
However there is no need to open a bug report about this now. I have already done it way back.

Disambiguator template keyword for a template member of a template: when exactly?

A question regarding template disambiguator was given here:
template disambiguator
and in the answer we can read:
ISO C++03 14.2/4
When the name of a member template specialization appears after . or -> in a postfix-expression, or after nested-name-specifier in a qualified-id, and the postfix-expression or qualified-id explicitly depends on a template-parameter (14.6.2), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template.
Now here comes my conrete example that I don't quite understand:
template <class T>
class Base {
public:
template <int v>
static int baseGet() {return v;}
class InnerA {
public:
template <int v>
static int aget() {return v;}
};
class InnerB {
public:
typedef Base BaseType;
typedef BaseType::InnerA OtherType;
template <int v>
static int baseGet() {return BaseType::baseGet<v>();} //(A)
template <int v>
static int aget() {return OtherType::aget<v>();} //(B)
};
};
It obviously fails to compile. You need template in the line (B): OtherType::template aget<v>();.
However, both g++ (4.4.3) and clang++ (2.9) don't complain about the lack of template in the line (A). Why? BaseType depends on the type T, does it not? Is it a small depart from the standard by those compilers, or do I misunderstand something in the standard?
They implement the C++0x specification, where Base is the current instantiation. And C++0x allows to omit template keyword in such a case. Since BaseType is a typedef for Base, when you say BaseType, that names the current instantiation too.
To quote the spec, since you seem to be interested in spec refs
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 the current instantiation or a non-dependent base class thereof.
and
A name refers to the current instantiation if it is [...]
in the definition of a [...] nested class of a class template, [...], the injected-class-name (Clause 9) of the class template or nested class
and (the modified 14.2/4 that you quoted)
[...] or the nested-name-specifier in the qualified-id refers to a dependent type, but the name is not a member of the current instantiation (14.6.2.1), the member template name must be prefixed by the keyword template. [...]
Note: In C++03 your code is ill-formed because both BaseType and OtherType are dependent. The spec says:
A type is dependent if it is [...]
a template parameter
a qualified-id with a nested-name-specifier which contains a class-name that names a dependent type
a template-id in which either the template name is a template parameter or any of the template arguments is a dependent type
(note that Base is equivalent to Base<T>, which is the base on which Base and BaseType::InnerA are dependent types).
Note that "explicitly depends" in your quote is a pre-standard term, and was gotten rid of fairly lately (I believe it was at December1996). It basically meant (in this context) a qualified-id in which the qualifier is dependent or a class member access (a->x / a.x) where the a was dependent. After "explicitly depends" was removed from the draft, it was still lurking around at some places, and even C++0x has still references to "explicitly depends" in a note at 14.6.2p2:
the base class name B<T>, the type name T::A, the names B<T>::i and pb->j explicitly depend on the template-parameter.
Because OtherType is a nested dependent name while BaseType is not a nested type to begin with.
You need to use template for nested dependent types.
The keywords here are:
Dependent Type
Nested Type
If a type is both, then you've to use template.
OtherType is both dependent type (it depends on T) as well as nested type
BaseType is only dependent type (it depends on T). Its not a nested type.
BaseType does depend on the type T- but so does InnerB. In effect, from the perspective of any code inside BaseType<T>, it does not depend on T.

Another bug in g++/Clang? [C++ Templates are fun]

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 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.
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 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;