[temp.spec]/6[1] was added to the C++20 spec by implementation of P0692R1[2]: "The usual access checking rules do not apply to names in a declaration of an explicit instantiation or explicit specialization, with the exception of names appearing in a function body, default argument, base-clause, member-specification, enumerator-list, or static data member or variable template initializer." Meaning that programs (A) through (C) below are (arguably) well-formed: (A) (DEMO: https://wandbox.org/permlink/No9RdUGRQFinIPie) class A { class B {}; }; template<typename T> struct S {}; template<> struct S<A::B> {}; int main() {} (B) (DEMO: https://wandbox.org/permlink/Ki1iLlje2raKJoEb): class A { class B {}; }; template<typename T> void foo() {}; template<> void foo<A::B>() {} int main() {} (C) (DEMO: https://wandbox.org/permlink/AN06msZgJNmxB0HS) class A { class B {}; }; template<typename T> constexpr bool v = false; template<> constexpr bool v<A::B> = true; int main() {} However whilst GCC accepts (A), it rejects (B) and (C). > (gcc HEAD 11.0.0 20201121 (experimental)) > g++ prog.cc -Wall -Wextra -std=c++2a > error: 'class A::B' is private within this context As per [3], P0692R1 has been implemented in GCC ("Available in GCC: Yes"). --- References [1] https://timsong-cpp.github.io/cppwp/n4861/temp.spec#6 [2] P0692R1 - Access Checking on Specializations http://open-std.org/JTC1/SC22/WG21/docs/papers/2017/p0692r1.html) [3] https://gcc.gnu.org/projects/cxx-status.html
Somewhat related, we may note that GCC accepts-valid the following program, say (D): class A { class B {}; }; template<typename T, typename U> struct S {}; template<typename U> struct S<A::B, U> {}; template<typename T> struct S<T, A::B> {}; int main() {} which is valid as per [temp.class.spec]/10 (https://timsong-cpp.github.io/cppwp/n4861/temp.class.spec#10), which covers waiving of access checking for template arguments in the simple-template-id of partial specializations, a paragraph that was also added as part of P0692R1.
Confirmed.