std :: async无法调用受保护的基类方法

时间:2019-02-14 19:48:24

标签: c++ inheritance

我想制作一个派生类,该派生类可以使用std :: async调用基类方法。当我尝试执行此操作时,我收到一条编译器消息,指出它已受到保护,但是我将“ this”作为实例传递,该实例是我要执行的函数的派生类。

这是我的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();
}

使用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);

为什么std :: async使受保护的方法无法从派生类访问?使用异步从派生类中调用基类方法有哪些替代方法?

2 个答案:

答案 0 :(得分:4)

您必须使用&Derived::threadFunc而不是&Base::threadFunc,因为当成员函数在基类中受保护时,您必须使用当前上下文的类。

(更新:这与我之前写的this指针类型无关。这是胡扯。)

LLVM甚至在错误消息中说明了解决方法:

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.

答案 1 :(得分:2)

有一个关于受保护成员访问的特殊规则。 Derived没有访问其基类的受保护成员的无条件能力。如果采用成员指针,则必须使用自身作为类(或Derived的派生类),而不是Base。标准参考是[class.protected] / 1:

  

当非静态数据时,将应用第14章前面所述之外的其他访问检查   成员或非静态成员函数是其命名类(14.2)的受保护成员。如前所述   较早的时候,授予访问受保护成员的权限是因为引用发生在某些朋友或成员中   类C。如果访问是为了形成指向成员(8.3.1)的指针,则嵌套名称说明符应表示C或   从C派生的类。所有其他访问都涉及一个(可能是隐式的)对象表达式(8.2.5)。在这种情况下,   对象表达式的类别应为C或从C派生的类别。

换句话说,您必须写&Derived::threadFunc而不是&Base::threadFunc。名称查找将在threadFunc中找到Base,但是结果指向成员的指针只能用于类型为Derived的对象或更多派生类中。这允许Derived对象使用threadFunc访问属于Base的自身内部,但是阻止其观察从Base派生的其他类中的内部。 / p>