Waiving of access checking rules for explicit function template specializations in C++20

Viewed 101

[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?
1 Answers

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:

(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.

Related