I have tested these several templates to show the validity of the template instantiation:
template <typename T>
using LRef = T&;
template <typename T>
using Ptr = T*;
template <typename>
requires false
class A {};
// ...
using T0 = LRef<int>; // expected ok
// using T1 = LRef<void>; // expected error, forming void& which is invalid
using T2 = Ptr<void>; // expected ok
// using T3 = Ptr<int&>; // expected error, forming int& * which is invalid
// using T4 = A<int>; // expected error, unsatisfied constraint resulting in invalid instantiation
However,
template <typename T>
struct Box { T value; };
template <typename T, typename U = T>
struct Derive : T, U {};
// ...
using T5 = Derive<int, float>; // unexpected no-error, but should be invalid
// template struct Derive<int, float>; // expected error
using T6 = Derive<Box<int>, Box<float>>; // expected ok
template struct Derive<Box<int>, Box<float>>; // expected ok
using T7 = Derive<Box<int>>; // unexpected no-error, but should be invalid
// template struct Derive<Box<int>>; // expected error
void foo() {
// T5 {}; // error
// T7 {}; // error
}
type alias declarations of T5 and T7 have no error, which are unexpected. I suppose that they should be invalid since the explicit instantiation with the same template arguments and the usage of type alias has errors.
Is this the correct behavior in standard C++ or just not implemented properly? These 3 major compilers (GCC, Clang, and MSVC) show no compilation error with this program. https://godbolt.org/z/hcbszo49a