Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access to protected constructor of base class

A derived class can call a protected base class constructor in its ctor-initializer, but only for its own base class subobject, and not elsewhere:

class Base {
  protected:
    Base() {}
};

class Derived : Base {
  Base b;
  public:
    Derived(): Base(),    // OK
               b() {      // error
        Base b2;          // error
    }
};

What does the standard say about this? Here is [class.protected]/1:

An additional access check beyond those described earlier in Clause 11 is applied when a non-static data member or non-static member function is a protected member of its naming class (11.2) As described earlier, access to a protected member is granted because the reference occurs in a friend or member of some class C. If the access is to form a pointer to member (5.3.1), the nested-name-specifier shall denote C or a class derived from C. All other accesses involve a (possibly implicit) object expression (5.2.5). In this case, the class of the object expression shall be C or a class derived from C. [ Example: ...

Is there an object expression involved when calling a constructor? There isn't, is there? So where in the standard is access control for protected base class constructors described?

like image 399
Brian Bi Avatar asked Jul 08 '14 15:07

Brian Bi


2 Answers

The protected access only applies to parent members of your own current object type. You don't get public access to the protected members of other objects of the parent type. In your example you only get access to the default base contructor as part of a Derived, not when it's a standalone object as b.

Let's break down the quote you posted from the standard (11.4/1). We'll assume that C in the standard corresponds to your Derived class:

An additional access check beyond those described earlier in Clause 11 is applied when a non-static data member or non-static member function is a protected member of its naming class (11.2).

So the base class constructor is effectively a non-static member function of its naming class (B) here, so this clause applies so far.

As described earlier, access to a protected member is granted because the reference occurs in a friend or member of some class C.

Member (constructor) of C so we're still good here.

If the access is to form a pointer to member (5.3.1), the nested-name-specifier shall denote C or a class derived from C.

This is not a pointer to member so this doesn't apply.

All other accesses involve a (possibly implicit) object expression (5.2.5).

The standard then asserts that all other possible accesses must involve an object expression.

In this case, the class of the object expression shall be C or a class derived from C.

Finally the standard states that the class of the expression must be C or a further derived class. In this case your expression Base() is in fact a C, calling the parent constructor (think about it as this->Base(). The expression b is clearly of type Base (that's the explicitly declared type of the member b, think about this->b->Base()). Now we do the check: Is Base a C or child of C? It is not, so the code is not legal.

like image 98
Mark B Avatar answered Nov 16 '22 23:11

Mark B


C++11 §11.2/5:


A member m is accessible at the point R when named in class N if

  • m as a member of N is public, or

  • m as a member of N is private, and R occurs in a member or friend of class N, or

  • m as a member of N is protected, and R 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 public, private, or protected, or

  • there exists a base class B of N that is accessible at R, and m is accessible at R when named in class B.

For your constructor invocation

Base b2;

the 3rd point above applies. m is the Base constructor. N, the naming class, is Base. m as a member of Base is protected, and the declaration occurs in a member of class Derived derived from Base, but it's not the case that the Base constructor as a member of Derived is public, private or protected: it’s simply not a member of Derived, constructors are not implicitly inherited.

I think the language “is public, private, or protected” is pretty awkward; I can only surmise that it’s the result of some evolution of this paragraph.

I have yet to find an explanation of how formally the protected Base constructor is accessible in a member initializer list in Derived, but then I just started looking at this for this question.


Update: I fail to find any language in the standard pertaining to access to constructors in an initializer list, and I fail to find any Defect Report about it. It’s quite possibly a defect.

like image 41
Cheers and hth. - Alf Avatar answered Nov 16 '22 21:11

Cheers and hth. - Alf