C++ boost::bind 表示无法访问的基类

C++ boost::bind says inaccessible base class

本文关键字:访问 基类 表示 boost bind C++      更新时间:2023-10-16

我正在尝试使用boost::bind来调用类中的成员函数。 通常这工作正常,但在这种特殊情况下,我的编译器 (GCC) 抱怨我试图使用无法访问的基类,而我不是。

下面是一些演示该问题的代码。 我做错了什么?

#include <iostream>
#include <boost/bind.hpp>
#include <boost/function.hpp>
class base
{
    protected:
        void test()
        {
            std::cout << "base::test()n";
        }
};
class derived: virtual protected base
{
    public:
        using base::test;
};
int main(void)
{
    derived d;
    // This works, calling derived::test(), which in turn calls base::test()
    d.test();
    // This does not work, saying 'base' is an inaccessible base of 'derived',
    // but I am not binding base::test, I am binding derived::test.
    boost::function<void()> fn;
    fn = boost::bind(&derived::test, d);
    fn();
    return 0;
}

using声明不定义函数。它"声明一个名称"(不是函数!),并取消隐藏基本名称。确实,声明本身具有自己的可访问性级别,这就是为什么您首先使用它的原因,但要再次强调:using 声明不会声明新的成员函数。例如 C++11 7.3.3/11:

使用声明声明

的实体应根据使用声明时的定义在使用它的上下文中已知。

在您的情况下,"它的定义"仍然是void base::test(){}的,这是派生类已知的实体并由&derived::test引用。您无法从中获取类型 void(derived:**)() 的函数指针,因为没有这样的函数。

当你说&derived::test时,使用&运算符,其工作原理如下(5.3.1/3):

一元&运算符的结果是指向其操作数的指针。操作数应为左值或限定 ID。如果操作数是一个限定 id,命名某个Cm T类的非静态成员,则结果的类型为"指向类型 T 的类 C 的成员的指针",并且是指定 C::m 的 prvalue。

在我对上述逻辑的解释中,&derived::test"命名类base的非静态成员test"。[感谢@DyP:]在 10.2/3 中形式化:

C [...] 中为 f 设置的查找由 [...] 声明集 [...] 组成。在声明集中,use-声明由它们指定的成员替换

如果你真的想要,你可以提供一个这样的包装器:

class derived : protected base
{
public:
    void test() { base::test(); }
};

(奇怪的是,该解决方案实际上确实隐藏了基本函数,这正是我们想要的。我们没有使用using,而是使用显式限定名来引用基函数。

调用函数时,需要将隐式this参数转换为基类。using声明不会将test类型从 base::* 调整为 derived::*

您可以手动执行此类调整:

static_cast< void (derived::*)() >( &derived::test )

但这同样要求基地是可访问的。因此,完整的解决方案是将上述static_cast封装在派生类中。否则,它应该是一个可访问的基地。