Issue
[temp.spec]/6 was added to C++20 by implementation of P0692R1 (Access Checking on Specialization) [emphasis mine]:
[temp.spec]/6 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. [ Note: In particular, the template arguments and names used in the function declarator (including parameter types, return types and exception specifications) may be private types or objects that would normally not be accessible. — end note ]
which, afaict, would make the following program well-formed as of C++20:
class A { class B {}; };
template<typename T> void foo() {};
template<> void foo<A::B>() {}
int main() {}
However, both GCC (HEAD 11.0.0 20201121; DEMO) and Clang (HEAD 12.0.0; DEMO) rejects the program above (for -std=c++20
/-std=c++2a
), citing an private access violation
GCC:
'class A::B' is private within this context
Clang:
error: 'B' is a private member of 'A'
GCC lists P0692R1 as implemented:
whereas Clang lists its implementation of P0692R1 as Partial:
Question
- What is the correct behaviour in C++20 here, is the program above well-formed (GCC bug / Clang not fully implemented) or are GCC and Clang correct to reject it?
Solution
What is the correct behaviour in C++20 here, is the program above well-formed (GCC bug / Clang not fully implemented) or are GCC and Clang correct to reject it?
As already cited in the OP, [temp.spec]/6 supports this feature, and the program is well-formed. Particularly, all of snippets (A) through (D) below are well-formed:
// (A)
class A { class B {}; };
template<typename T> struct S {};
template<> struct S<A::B> {};
// (B)
class A { class B {}; };
template<typename T> void foo() {};
template<> void foo<A::B>() {}
// (C)
class A { class B {}; };
template<typename T>
constexpr bool v = false;
template<>
constexpr bool v<A::B> = true;
// (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> {};
GCC is wrong to reject (B) and (C) (correctly accepts (A) and (D)), and Clang is wrong(1) to reject (B) through (D) (correctly accepts (A)).
Bug reports:
- GCC (confirmed): [C++20][P0692R1] Access checking not waived for declarations of explicit specializations of function and variable templates
- Clang: [P0692R1] Access checking not waived for declarations of explicit specializations of function and variable templates
(1) Note that Clang (as highlighted in the OP) has marked the implementation status for P0692R1 as "Partial", so this may not be a Clang bug but rather a feature yet to be implemented C++2a/C++20.
Answered By - dfrib