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?
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
Xoutside 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 classXor 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.
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