Injected class name compiler discrepancy
It appears that clang
is wrong in this case. The relevant exception I was looking for is in [class.qual]/2:
2 In a lookup in which function names are not ignored and the nested-name-specifier nominates a class C:
(2.1)
if the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name of C, or[...]
the name is instead considered to name the constructor of class C.
The standard has a near-equivalent (non-normative, obviously) example:
struct A { A(); };
struct B: public A { B(); };
A::A() { }
B::B() { }
B::A ba;// object of type A
A::A a;// error, A::A is not a type name
struct A::A a2;// object of type A
However, clang
actually issues a correct diagnostic in this case:
error: qualified reference to 'A' is a constructor name rather than a type wherever a constructor can be declared
Perhaps clang
interprets the line In a lookup in which function names are not ignored
as In a lookup in which a constructor declaration is valid
, but that doesn't seem to be a correct interpretation.
There is an existing bug for this in the clang
bugzilla.
What happens when Injected-Class-Name occurs? (C++)
So what is really happening in the code? Is X* p turned into
X::X*
p?
Basically. The name lookup rules start in the narrowest scope. When you do X* p;
in f
is looks in f
's scope and doesn't find anything. Then it checks X
's scope since f
is scoped to X
. It finds X
since it is injected into the class scope so it stops there and you get the class type.
When you do ::X* q;
then ::X
says look for X
in the global namespace, and there is finds a variable, not a type so you get an error.
Why injected-class-name is sometimes not treated as a template name in a class template?
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.
Ambiguous injected class name is not an error
I found it! It's right there in the standard! I was right! It should be ambiguous!
Clause 14.6.1 Paragraph
A lookup that finds an injected-class-name (10.2) 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 followed by a template-argument-list, 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]
Bottom line: This is yet another Microsoft compiler BUG. Disabling language extensions doesn't help either.
Injected-class-names of class templates
gcc
is correct; your snippet is ill-formed!
// reduced testcase
template<class T>
class A { };
int main () {
A<float>::A<int> x; // ill-formed, bug in `clang` and `icc`
}
In the above reduced testcase we have a nested-name-specifier, A<float>::
, followed by an unqualified-id A
, which is then followed by some gibberish (<int>
).
This is because the context in which the nested-name-specifier appears mandates that, during a look-up, function names are included (meaning that the constructor is found first, and the expression is ill-formed).
Relevant Bug Reports:
- llvm.org/bugs/ - #8263; Incorrect constructor name resolution
How to circumvent the "problem"?
There are contexts in which member names that are looked up through a nested-name-specifier (that nominates a class) shall not include functions (hence, contexts where the constructor is not found), below are a few examples:
template<class T>
struct A {
typedef T value_type;
};
struct A<float>::A<int> x; // ok, context: elaborate-type-specifier
typename A<float>::A<int> (); // ok, context: [expr.type.conv]p1
A<float>::A::value_type x; // ok, context: nested-name-specifier
struct X : A<float>::A<int> { }; // ok, context: base-specifier
What does the Standard say?
3.4.3.1p2
Class members[class.qual]
In a lookup in which function names are not ignored88 and the nested-name-specifier nominates a class C:
- if the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name of C (Clause 9), or
- in a using-declaration (7.3.3) that is a member-declaration, if the name specified after the nested-name-specifier is the same as the identifier or the simple-template-id's template-name in the last component of the *nested-name-specicifier,
the name is instead considered to name the constructor of class C.
[ Note: ... ]
Such a constructor name shall be used only in the declarator-id of a declaration that names a constructor or in a using-declaration.
88. Lookups in which function names are ignored include names appearing in a nested-name-specifier, an elaborated-type-specifier, or a base-specifier.
14.6.1p2
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 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<>
.
Class Name Injection and Constructors
Clang-5 is very much correct. Over at [class.qual]/2:
In a lookup in which function names are not ignored and the
nested-name-specifier nominates a class C:
- if the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name of C
- ...
the name is instead considered to name the constructor of class C.
As for the other part of the question. Yes, it's definitely worth it to submit bug reports. Standard compliance (or at least more diagnostics towards it) are to be encouraged IMO.
Where am I wrong here? The lookup for the name Node finds the injected-class-name for the struct Node
The first sentence of [basic.scope.pdecl]/7 makes clear that that paragraph deals with a class first declared in an elaborated-type-specifier; it is therefore not applicable to the struct Node
in struct Node* Next;
, because Node
is not first declared in that elaborated-type-specifier.
Instead, the unqualified name lookup for Node
does find the injected-class-name (see [basic.lookup.unqual]/p1 and 7.1) rather than ::Node
. You'll need to write some rather contrived code for this to matter, though. For example:
struct A {};
struct B : private A {};
struct C : public B {
struct A m_a; // error: A is the inaccessible injected-class-name, not ::A
struct ::A m_a2; // OK
};
Note that GCC accepts this code, which I believe is a bug.
Program being compiled differently in 3 major C++ compilers. Which one is right?
GCC is correct, at least according to C++11 lookup rules. 3.4.3.1 [class.qual]/2 specifies that, if the nested name specifier is the same as the class name, it refers to the constructor not the injected class name. It gives examples:
B::A ba; // object of type A
A::A a; // error, A::A is not a type name
struct A::A a2; // object of type A
It looks like MSVC misinterprets it as function-style cast expression creating a temporary C
with y
as a constructor parameter; and Clang misinterprets it as a declaration of a variable called y
of type C
.
Related Topics
C++ Template Function Default Value
Clion C++ Can't Read/Open .Txt File in Project Directory
Converting Steady_Clock::Time_Point to Time_T
What's the Standard/Official Name for Universal References
Wrap Overloaded Function via Std::Function
Why Isn't Operator Overloading for Pointers Allowed to Work
C++ Debug Builds Broke in Snow Leopard Xcode
Can Pointers Be of Different Sizes
Why Do I See 400X Outlier Timings When Calling Clock_Gettime Repeatedly
What Happens to the Memory Allocated by 'New' If the Constructor Throws
C++ Inheritance and Name Hiding
What Rules Does Compiler Have to Follow When Dealing with Volatile Memory Locations
Specifying Default Parameter When Calling C++ Function