Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can’t protected members be used by friends of derived classes?

The C++ standard states in [class.access/1] (emphasis mine):

A member of a class can be

  • private; that is, its name can be used only by members and friends of the class in which it is declared.
  • protected; that is, its name can be used only by members and friends of the class in which it is declared, by classes derived from that class, and by their friends (see [class.protected]).
  • public; that is, its name can be used anywhere without access restriction.

So why does the compiler raise this error in the following C++ program?

#include <iostream>

class B {
  protected:
    static int const i = 1;
};

class D: public B {
  public:
    void f();
    friend void g();
};

void D::f() {
  B b;
  std::cout << b.i;  // OK
}

void g() {
  B b;
  std::cout << b.i;  // error: 'i' is a protected member of 'B'
}

int main() {
  D d;
  d.f();
  g();
  return 0;
}

Notice that the protected data member B::i is declared static not to be subject to the further restrictions specific to protected non-static members in [class.access/class.protected-1] which would also raise the same error at the b.i access in the D::f member function as that in the g function.

Note. — I am using C++ 17 on the Clang 9.0.0 compiler.

like image 327
Maggyero Avatar asked Feb 11 '20 23:02

Maggyero


1 Answers

It appears to be a specification issue. The part that I have quoted is not in sync with another part which specifies the new correct behaviour followed by Clang (but not GCC nor MSVC surprisingly), [class.access/base-5] (emphasis mine):

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

Here friend is not mentioned for the derived class. But it used to be. It has been removed in C++ 17 because of defect report CWG 1873 (emphasis mine):

Protected member access from derived class friends

Section: 14.2 [class.access.base] Status: CD4 Submitter: Richard Smith Date: 2014-02-18

[Moved to DR at the May, 2015 meeting.]

According to 14.2 [class.access.base] paragraph 5,

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

  • 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

The granting of access via class P is troubling. At the least, there should be a restriction that P be visible at R. Alternatively, this portion of the rule could be removed altogether; this provision does not appear to be widely used in existing code and such references can be easily converted to use P instead of N for naming the member.

Notes from the June, 2014 meeting:

CWG noted that removing the friend provision would introduce an undesirable asymmetry between member functions of P and its friends. Instead, the intent is to require P to be a complete type at R.

Notes from the November, 2014 meeting:

Although the asymmetry is unfortunate, the difference between a reference in a member function and a reference in a friend is that the member function concretely determines which P is in view, while the friend could be befriended by a class that is a specialization of a class template and thus would require instantiations that would not otherwise occur. CWG thus decided simply to eliminate the friend case.

Proposed resolution, November, 2014:

Change bullet 5.3 of 14.2 [class.access.base] as follows:

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

  • 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…

I have filed a pull request on GitHub to correct this inconsistency: https://github.com/cplusplus/draft/pull/3672

like image 50
Maggyero Avatar answered Oct 13 '22 16:10

Maggyero