The way I interpret the Standard, the sample code is well-formed. (And yes, the friend
declarations make a big difference from the thing @Steve Townsend quoted.)
11.2p1: If a class is declared to be a base class for another class using the private
access specifier, the public
and protected
members of the base class are accessible as private
members of the derived class.
11.2p4: A member m is accessible when named in class N if
- m as a member of N is public, or
- m as a member of N is private, and the reference occurs in a member or friend of class N, or
- m as a member of N is protected, and the reference occurs in a member or friend of class N, or in a member or friend of a class P derived from N, where m as a member of P is private or protected, or
- there exists a base class B of N that is accessible at the point of reference, and m is accessible when named in class B.
11.4p1: A friend of a class is a function or class that is not a member of the class but is permitted to use the private and protected member names from the class.
There are no statements in Clause 11 (Member access control) which imply that a friend of a class ever has fewer access permissions than the class which befriended it. Note that "accessible" is only defined in the context of a specific class. Although we sometimes talk about a member or base class being "accessible" or "inaccessible" in general, it would be more accurate to talk about whether it is "accessible in all contexts" or "accessible in all classes" (as is the case when only public
is used).
Now for the parts which describe checks on access control in automatically defined methods.
12.1p7: An implicitly-declared default constructor for a class is implicitly defined when it is used to create an object of its class type (1.8). The implicitly-defined default constructor performs the set of initializations of the class that would be performed by a user-written default constructor for that class with an empty mem-initializer-list (12.6.2) and an empty function body. If that user-written default constructor would be ill-formed, the program is ill-formed.
12.6.2p6: All sub-objects representing virtual base classes are initialized by the constructor of the most derived class (1.8). If the constructor of the most derived class does not specify a mem-initializer for a virtual base class V
, then V
's default constructor is called to initialize the virtual base class subobject. If V
does not have an accessible default constructor, the initialization is ill-formed.
12.4p5: An implicitly-declared destructor is implicitly defined when it is used to destroy an object of its class type (3.7). A program is ill-formed if the class for which a destructor is implicitly defined has:
- a non-static data member of class type (or array thereof) with an inaccessible destructor, or
- a base class with an inaccessible destructor.
12.8p7: An implicitly-declared copy constructor is implicitly defined if it is used to initialize an object of its class type from a copy of an object of its class type or of a class type derived from its class type. [Note: the copy constructor is implicitly defined even if the implementation elided its use (12.2).] A program is ill-formed if the class for which a copy constructor is implicitly defined has:
- a nonstatic data member of class type (or array thereof) with an inaccessible or ambiguous copy constructor, or
- a base class with an inaccessible or ambiguous copy constructor.
12.8p12: A program is ill-formed if the class for which a copy assignment operator is implicitly defined has:
- a nonstatic data member of
const
type, or
- a nonstatic data member of reference type, or
- a nonstatic data member of class type (or array thereof) with an inaccessible copy assignment operator, or
- a base class with an inaccessible copy assignment operator.
All these requirements mentioning "inaccessible" or "accessible" must be interpreted in the context of some class, and the only class that makes sense is the one for which a member function is implicitly defined.
In the original example, class A
implicitly has public
default constructor, destructor, copy constructor, and copy assignment operator. By 11.2p4, since class C
is a friend of class A
, all those members are accessible when named in class C
. Therefore, access checks on those members of class A
do not cause the implicit definitions of class C
's default constructor, destructor, copy constructor, or copy assignment operator to be ill-formed.