13

我想bind()从派生类中获得我的基类版本的函数。该功能在底座中标记为受保护。当我这样做时,代码在 Clang (Apple LLVM Compiler 4.1) 中顺利编译,但在 g++ 4.7.2 和 Visual Studio 2010 中都出现错误。错误类似于:“'Base::foo': cannot访问受保护的成员。”

这意味着引用的上下文实际上是在内部bind(),当然该函数被视为受保护的。但是不应该bind()继承调用函数的上下文——在这种情况下,Derived::foo()——因此将基方法视为可访问的?

下面的程序说明了这个问题。

struct Base
{
protected: virtual void foo() {}
};

struct Derived : public Base
{
protected:
    virtual void foo() override
    {
        Base::foo();                        // Legal

        auto fn = std::bind( &Derived::foo, 
            std::placeholders::_1 );        // Legal but unwanted.
        fn( this );

        auto fn2 = std::bind( &Base::foo, 
            std::placeholders::_1 );        // ILLEGAL in G++ 4.7.2 and VS2010.
        fn2( this );
    }
};

为什么行为上的差异?哪个是对的?错误编译器有什么解决方法?

4

2 回答 2

11

答案:参见boost::bind with protected members & context引用标准的这一部分

当非静态数据成员或非静态成员函数是其命名类 (11.2)105 的受保护成员时,将应用第 11 节中所述之外的附加访问检查)如前所述,授予对受保护成员的访问权限是因为引用发生在某个类 C 的朋友或成员中。如果访问要形成指向成员的指针 (5.3.1),则嵌套名称说明符应命名 C 或从 C 派生的类。所有其他访问都涉及 (可能是隐式的)对象表达式(5.2.5)。在这种情况下,对象表达式的类应为 C 或从 C 派生的类。

解决方法:创建foo一个public成员函数

#include <functional>

struct Base
{
public: virtual void foo() {}
};
于 2013-01-24T19:55:08.347 回答
10

这与bind. 由于已经引用了标准@rhalbersma 的部分,因此在任何情况下,该表达式&Base::foo在 的非好友成员中都是非法的Derived

但是,如果您的意图是做与调用等效的事情Base::foo();,那么您将面临更大的问题:指向成员函数的指针总是调用虚拟覆盖。

#include <iostream>

class B {
public:
    virtual void f() { std::cout << "B::f" << std::endl; }
};

class D : public B {
public:
    virtual void f() { std::cout << "D::f" << std::endl; }
};

int main() {
    D d;
    d.B::f();   // Prints B::f

    void (B::*ptr)() = &B::f;
    (d.*ptr)(); // Prints D::f!
}
于 2013-01-24T20:27:34.810 回答