2

Here is my codes in file source.cpp:

class B
{
  friend class F;
protected:
  int protectedIntB;
};

class D : public B {};

class F
{
public:
  int f(D &d) {return ++d.protectedIntB;}
};

When I compile above codes with g++ -c -Wall -pedantic -std=c++11 source.cpp and cl /c source.cpp, both compilers compile successfully. However, when I make D inherits from B using protected instead of public:

class D : protected B {};

This time, gcc compiles successfully while cl gives an error says that B::protectedIntB is inaccessible in return ++d.protectedIntB;.

Another situation is replacing public with private:

class D : private B {};

This time, both compilers yield errors. By the way I'm using gcc version 5.3.0 built by mingw-w64 and cl version 19.00.24210 from VS2015.

Here comes my question:

How does friend class of the base class access members of that base class through objects of class derived from the base class, and why gcc and cl handle it differently?

Edit:

Thanks to songyuanyao and Brian, it seems a bug in gcc 5.3.0 in the protected case. Only the public case should be compiled successfully, and gcc 6.1.0 also works fine.

Community
  • 1
  • 1
Carousel
  • 728
  • 4
  • 13

3 Answers3

3

According to [class.access.base]/5:

The access to a member is affected by the class in which the member is named. This naming class is the class in which the member name was looked up and found.

According to [class.access.base]/6:

If a class member access operator, including an implicit “this->,” is used to access a non-static data member or non-static member function, the reference is ill-formed if the left operand (considered as a pointer in the “.” operator case) cannot be implicitly converted to a pointer to the naming class of the right operand.

Therefore, in your particular case, in order to access d.protectedIntB, both of the following must be true:

  • You have to have access to protectedIntB as a member of B, since B is the class in which the name protectedIntB was found. (Note: this can be altered by redeclaring the member in the derived class using a using-declaration; in that case the derived class would be controlling.)

  • You have to have access to B as a base of D, i.e., be able to convert D* to B*. If B is a public base of D, fine. If B is a protected base of D, an access check applies, which F::f fails since F is not a friend of D and is not a derived class of D.

Surprisingly, it seems that GCC is the compiler that's wrong in the protected case but this bug appears fixed. Note that Clang gives a much better diagnostic.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
3

If you make D inherits from B using protected or private instead of public, compilation should fail.

From the standard, $11.2/5 Accessibility of base classes and base class members [class.access.base]:

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

(5.4) there exists a base class B of N that is accessible at R, and m is accessible at R when named in class B. [ Example:

class B;
class A {
private:
  int i;
  friend void f(B*);
};
class B : public A { };
void f(B* p) {
  p->i = 1;         // OK: B* can be implicitly converted to A*,
                    // and f has access to i in A
}

— end example ]

For your 1st case, the base class B of D is accessible at F::f(), because it's public inheritance. And B::protectedIntB is accessible at F::f() because it's friend of class B.

If you change it to protected or private inheritance, the base class B of D is not accessible at F::f() again, then compilation should fail. Note F::f() is not friend of derived class D. It means if you make it friend of class D too, compilation will succeed.

BTW: I tried the protected inheritance with gcc here and it failed.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
0

If the code compiles on gcc 5.3.0 and does not compile on cl, probability is high that one of the two does not strictly enforce C++ standard. If I have to take a guess, for protected and private inheritance, you should get a compiler error. For the difference between different kind of inheritances see Difference between private, public, and protected inheritance for more details.

For the class F to be able to access the private members of class B, It should know that class D derives from class B which will only happen for public inheritance.

Community
  • 1
  • 1
cplusplusrat
  • 1,435
  • 12
  • 27