template<typename T>
struct A {
using U = typename T::U;
using V = typename T::V; //X
};
struct B {
using U = int;
void f() { A<B> a; } //1
//A<B> a; //2
using V = int;
};
This compiles on current GCC, Clang, MSVC and ICC (https://godbolt.org/z/dvExbxszn).
I would like to know whether this is actually specified to work in the standard. Specifically, where is the point of instantiation of A<B> allowing both B::U and B::V to be looked up?
If we use //2 instead of //1, all four compilers reject the code, but accept if //X is removed. For this case I understand that strict reading of the standard probably makes the program ill-formed even if //X is removed, because the point of instantiation should be above the definition of B. However following the suggested change in CWG 287, compilers allow lookup of A::U which was declared before the point requiring instantiation of A<B>.
With void f() { A<B> a; } however, the instantiation is required from a complete-class context and it seems to me that compilers assume that the point of instantiation is then after the definition of B, so that all names in B are available to lookup in the instantiation. Is this actually specified in the standard or is it a situation analogously to CWG 287 where compilers try to make the instantiations behave like non-template definitions would, against strict interpretation of the standard?