According to Template parameters and template arguments on cppreference.com:
A template argument for a type template parameter must be a type-id, which may name an incomplete type
That means that this example (copied from that page) is legal:
// Example 0
template <typename T>
struct X {};
struct A;
int main() {
X<A> x1;
}
and, I suppose, also this is legal:
// Example 1
template <typename T>
struct X {};
struct A {
X<A> x1;
};
int main() {
A a1;
}
Now, let's complicate a bit the code:
// Example 2
template <typename T>
struct X {
using type = typename T::type;
};
struct A {
using type = int;
X<A>::type x1;
};
int main() {
A a1;
}
In this case, A
is still an incomplete type when used as template argument, and the code compiles. Now, if I change the order of the lines in A
:
// Example 3
template <typename T>
struct X {
using type = typename T::type;
};
struct A {
X<A>::type x1; // ERROR HERE
using type = int;
};
int main() {
A a1;
}
the code does not compile anymore. Clang error message is something like no type named 'type' in 'A'
, but also GCC, ICC and MSVC return similar errors (see this demo on godbolt.com), and this seems almost identical to that of typedefs on CRTP, discussed here.
Anyway, the behavior seems reasonable: compilers use all the properties of A
defined at the point X<A>
is used.
But what does the C++14 (or later) standard say about it? Is it legal to rely on the behavior of the working Example 2?