2

I want to make a derived class that can call a base class method using std::async. When I try to do this I get a compiler message saying it is protected, but I am passing "this" as the instance which is a derived class of the function I want to execute.

Here is my MCVE:

#include <iostream>
#include <thread>
#include <future>

class Base
{
protected:
    bool threadFunc()
    {
        std::cout << "threadFunc called" << std::endl;
        return true;
    }
};

class Derived : public Base
{
public:
    void callBaseMethodAsync()
    {
        std::future<bool> results = std::async(std::launch::async, &Base::threadFunc, this);
    }
};

int main()
{
    Derived test;
    test.callBaseMethodAsync();
}

Which results in this compiler error message with gcc 4.8:

g++ -o source.o -c -std=c++11 source.cpp
source.cpp: In member function 'void Derived::callBaseMethodAsync()':
source.cpp:8:10: error: 'bool Base::threadFunc()' is protected
    bool threadFunc()
        ^
source.cpp:20:75: error: within this context
        std::future<bool> results = std::async(std::launch::async, &Base::threadFunc, this);

Why is std::async making a protected method not accessible from the derived class? What are some alternate ways to achieve using async to call the base class method from the derived class?

dmoody256
  • 538
  • 4
  • 12
  • 2
    Downvoter, what kind of [mcve] you are missing? – Slava Feb 14 '19 at 19:52
  • 1
    very related: https://stackoverflow.com/questions/16785069/why-cant-a-derived-class-call-protected-member-function-in-this-code I'm not sure if it is guaranteed to work but `std::async(std::launch::async, &Derived::threadFunc, this)` gets it to compile. – NathanOliver Feb 14 '19 at 19:54

2 Answers2

4

You have to use &Derived::threadFunc instead of &Base::threadFunc, because you have to use the class of the current context when the member function is protected in the base class.

(Update: This has nothing to do with the this pointer type as I wrote earlier. This was nonsense.)

LLVM even explains how to fix this in the error message:

ov@abook:~/tmp>g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 10.0.0 (clang-1000.11.45.5)
Target: x86_64-apple-darwin17.7.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
ov@abook:~/tmp>g++ -std=c++11 t.cpp 
t.cpp:20:75: error: 'threadFunc' is a protected member of 'Base'
        std::future<bool> results = std::async(std::launch::async, &Base::threadFunc, this);
                                                                          ^
t.cpp:8:14: note: must name member using the type of the current context 'Derived'
        bool threadFunc()
             ^
1 error generated.
Johannes Overmann
  • 4,914
  • 22
  • 38
2

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 class C. If the access is to form a pointer to member (8.3.1), the nested-name-specifier shall denote C or a class derived from C. All other accesses involve a (possibly implicit) object expression (8.2.5). In this case, the class of the object expression shall be C or a class derived from C.

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.

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