Reputation: 516
I finally managed to boil down my monolithic template library compilation failure to a simple test case. MSVC disagrees with Clang and GCC on the code being malformed, mostly due to name lookup. Unfortunately, I am not adept at reading the C++ specification, and I tend to believe Clang and GCC when it comes to conformance, but could you help me finding the relevant part of the specs so I may file a bug?
namespace A
{
template <int _I, int _II>
struct X {};
}
namespace B
{
template <int _J>
struct X {};
// Clang: OK
// GCC: OK
// MSVC: OK
template <int _K>
struct Y
{
Y(const A::X<_K, _K>&,
const X<_K>&) {}
};
// Clang: OK
// GCC: OK
// MSVC: ERROR
template <int _K>
struct Z : A::X<_K, _K>
{
Z(const A::X<_K, _K>&,
const X<_K>&) {}
};
// Clang: ERROR
// GCC: ERROR
// MSVC: ERROR
struct Q : A::X<1, 1>
{
Q(const A::X<1, 1>&,
const X<1>&) {}
};
}
int main()
{
A::X<1, 1> ax;
B::X<1> bx;
B::Z<1> bz(ax, bx);
return 0;
}
Three cases are:
What is going on here?
Upvotes: 3
Views: 109
Reputation: 303027
The problem you're running into is the injected-class-name. From [class]:
The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name. For purposes of access checking, the injected-class-name is treated as if it were a public member name.
With unqualified lookup in a class definition, the first stop is in the scope of the class and its members. From [basic.lookup.unqual]:
A name used in the definition of a class
X
outside of a member function body, default argument, exception-specification, brace-or-equal-initializer of a non-static data member, or nested class definition shall be declared in one of the following ways:
(7.1) — before its use in classX
or be a member of a base class ofX
(10.2), or
(7.2) — [...]
We're looking for X
. There is no Q::X
(Q
's own scope) but there is an A::X<1,1>::X
(base class scope), so lookup stops there and we never consider the enclosing namespace of Q
. That's why we find A::X<>
instead of B::X<>
.
With Z
, the situation is a little bit different. Now the base class is a dependent class template, and unqualified lookup will not look in dependent base classes. From [temp.dep]:
In the definition of a class or class template, the scope of a dependent base class (14.6.2.1) is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member.
So while the name A::X<_K,_K>::X
exists, it is not considered, so lookup continues into the enclosing namespace and B::X<>
is found.
In both cases, gcc/clang are correct.
Upvotes: 3
Reputation: 69882
The gcc's error message provides the clue:
38 : error: wrong number of template arguments (1, should be 2)
const X<1>&) {}
^
4 : note: provided for 'template<int _I, int _II> struct A::X'
struct X {};
^
What is going on here?
A template class is not code. It's a recipe for creating code. two-phase lookup will handle the definition of Z<>
when you use it (at which point there had better be an X<i>
available).
Q
is a concrete class, so there's no two-phase lookup involved. X<i>
must exist at that point - and it doesn't.
As ever, clang and gcc are correct. MSVC is non-conforming.
Upvotes: 0