Why can't a derived class call protected member function in this code?
Even though access control in C++ works on per-class basis (as opposed to per-instance basis), protected
access specifier has some peculiarities.
The language specification wants to ensure that you are accessing a protected member of some base subobject that belongs to the derived class. You are not supposed to be able access protected members of some unrelated independent objects of base type. In particular, you cannot access protected members of freestanding objects of base type. You are only allowed to access protected members of base objects that are embedded into derived objects as base subobjects.
For this reason, you have to access protected members through pointer->member
syntax, reference.member
or object.member
syntax, where the pointer/reference/object refers to the derived class.
This means that in your example, protected member somethingProtected()
is not accessible through Base
objects, Base *
pointers or Base &
references, but it is accessible through Derived
objects, Derived *
pointers and Derived &
references. Your plain somethingProtected()
access is allowed, since it is just a shorthand for this->somethingProtected()
where this
is of type Derived *
.
b.somethingProtected()
violates the above requirements.
Note that in accordance with the above rules in
void Derived::somethingDerived()
{
Base *b = this;
b->somethingProtected(); // ERROR
this->somethingProtected(); // OK
}
the first call will also fail while the second one will compile, even though both are trying to access the same entity.
Accessing protected members in a derived class
A class can only access protected members of instances of this class or a derived class. It cannot access protected members of instances of a parent class or cousin class.
In your case, the Derived
class can only access the b
protected member of Derived
instances, not that of Base
instances.
Changing the constructor to take a Derived
instance will solve the problem.
C++: Why Protected Constructor Cannot be Accessed in the Derived Class?
protected
members could be accessed from derived class, but only when through the derived class.
A
protected
member of a class is only accessible
- ...
- to the members
and friends (until C++17)
of any derived class of that class, but only when the class of the object through which the protected member is accessed is that derived class or a derived class of that derived class:
So you can't create an indpendent object of base class even in member functions of derived class.
Put it in another way, the protected
members of the current instance of derived class could be accessed, but protected
members of independent base class can't. E.g.
class A {
protected:
int x;
public:
A() : x(0) {}
};
class B : public A {
public:
void g() {
this->x = 42; // fine. access protected member through derived class
A a;
a.x = 42; // error. access protected member through base class
}
};
Why can’t protected members be used by friends of derived classes?
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 memberor friendof 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
How can I access base class's protected members through derived class?
There is not more to it than you already discovered. Derived instances may acces their protected members and those of other derived instances but not those of base class instances. Why? Because thats how protected
works by definition.
For more details I refer you to cppreference (emphasize mine):
A protected member of a class Base can only be accessed
1) by the members and friends of Base
2) by the members and friends (until
C++17) of any class derived from Base, but only when operating on an
object of a type that is derived from Base (including this)
Why can't I access a protected member from an instance of a base class?
You can access the protected
members of a base class only through a pointer or reference to an object of the derived type.
If you change
void copy_a_from_bar(bar& o){
a = o.a;
}
to
void copy_a_from_bar(bar& o){
foo& foo_ref = o;
a = o.a; // OK. Accessing it through `bar&`
a = foo_ref.a; // Not OK. Accessing it through `foo&`
}
you will see the same error.
This SO answer gives an indication of why allowing access to the protected
members of the base class will be a potential breach of the protected
status of the base class members.
Say you have:
class baz : public foo
{
void update_a(foo& f)
{
f.a = 20;
}
};
and use:
bar x;
baz z;
z.update_a(x);
If this were allowed, baz
will be able to change the values of members of bar
. That is not good.
std::async can't call protected base class method
There is a special rule regarding protected member access. Derived
does not have the unconditional ability to access protected members of its base class. In the case of taking a pointer-to-member, it has to use itself as the class (or a derived class of Derived
), and not Base
. The standard reference is [class.protected]/1:
An additional access check beyond those described earlier in Clause 14 is applied when a non-static data
member or non-static member function is a protected member of its naming class (14.2). As described
earlier, access to a protected member is granted because the reference occurs in a friend or member of some
classC
. If the access is to form a pointer to member (8.3.1), the nested-name-specifier shall denoteC
or a
class derived fromC
. All other accesses involve a (possibly implicit) object expression (8.2.5). In this case,
the class of the object expression shall beC
or a class derived fromC
.
In other words, you have to write &Derived::threadFunc
instead of &Base::threadFunc
. Name lookup will find threadFunc
in Base
, but the resulting pointer-to-member can only be used on an object of type Derived
or a more derived class. This allows a Derived
object to use threadFunc
to access its own internals that are part of Base
, but prevents it from observing those internals in other classes derived from Base
.
Related Topics
Installing C++ Libraries on Os X
How to Check If an Object's Type Is a Particular Subclass in C++
How to Store Extremely Large Numbers
Using Member Variable in Lambda Capture List Inside a Member Function
How to Create Timer Events Using C++ 11
Std::Vector to Boost::Python::List
How to Specify Preference of Library Path
How to Parse Ini File with Boost
Which C++ Standard Is the Default When Compiling with G++
Why Is Allocator::Rebind Necessary When We Have Template Template Parameters
How to Use Setprecision in C++
What Is a Copy Constructor in C++