C++ boost::bind says inaccessible base class

2019-04-13 07:06发布

问题:

I am trying to use boost::bind to call a member function in a class. Normally this works fine, but in this particular case my compiler (GCC) is complaining that I am trying to use an inaccessible base class when I am not.

Here is some code that demonstrates the problem. What am I doing wrong?

#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;
}

回答1:

The using declaration doesn't define a function. It "declares a name" (not a function!), and unhides base names. It's true that the declaration itself has its own accessibility level, which is why you're using it at all in the first place, but to stress again: The using declaration does not declare a new member function. E.g. C++11 7.3.3/11:

The entity declared by a using-declaration shall be known in the context using it according to its definition at the point of the using-declaration.

"Its definition", in your case, is still void base::test(){}, and that is the entity that is made known to the derived class and referred to by &derived::test. You can't get a function pointer of type void(derived:**)() from this because there is no such function.

When you say &derived::test, the &-operator is used, which works like this (5.3.1/3):

The result of the unary & operator is a pointer to its operand. The operand shall be an lvalue or a qualified-id. If the operand is a qualified-id naming a non-static member m of some class C with type T, the result has type “pointer to member of class C of type T” and is a prvalue designating C::m.

In my interpretation of the above logic, &derived::test "names the non-static member test of class base". [Thanks to @DyP:] The is formalized in 10.2/3:

The lookup set for f in C [...] consists of [...] the declaration set [...]. In the declaration set, using-declarations are replaced by the members they designate

If you really want, you can provide a wrapper like this:

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

(Curiously, that solution actually does hide the base function, which is what we want. Rather than using using, we use an explicitly qualified name to refer to the base function.)



回答2:

When the function is called, the implicit this argument needs to be converted to the base class. The using declaration does not adjust the type of test from base::* to derived::*.

You can perform such adjustment manually:

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

but this likewise requires the base to be accessible. So the complete solution would be to encapsulate the above static_cast inside the derived class. Otherwise it should be an accessible base.