[temp.explicit] contains this wording:
The usual access checking rules do not apply to names used to specify explicit instantiations. [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 which would normally not be accessible and the template may be a member template or member function which would not normally be accessible. —end note ]
Why are those rules disabled specifically for this case? In the extreme, this allows for arbitrary access of any private member of any class in a well-defined way (demo - zero warnings):
struct A { private: int member; }; template<typename Tag, typename Tag::type M> struct Rob { friend typename Tag::type get(Tag) { return M; } }; // tag used to access A::member struct A_member { typedef int A::*type; friend type get(A_member); }; template struct Rob<A_member, &A::member>; int main() { A a; a.*get(A_member()) = 42; // write 42 to it std::cout << "proof: " << a.*get(A_member()) << std::endl; }
So that's the downside of this rule. What's the upside? Why do we need this hole to avoid access checking?