std::bind()-ing 从派生类的成员函数中获取受保护的基本成员函数

std::bind()-ing a base protected member function from a derived class's member function

本文关键字:函数 成员 受保护 获取 bind -ing 派生 std      更新时间:2023-10-16

我想从派生类中bind()到我的基类版本的函数。该函数在底座中标记为受保护。当我这样做时,代码在Clang(Apple LLVM Compiler 4.1)中编译愉快,但在g ++ 4.7.2和Visual Studio 2010中都给出了错误。错误大致如下:"'Base::foo':无法访问受保护的成员。

这意味着引用的上下文实际上是在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 );
    }
};

为什么会有行为差异?哪个是正确的?对于出错的编译器,有哪些解决方法?

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

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

解决办法:使foo成为public成员函数

#include <functional>
struct Base
{
public: virtual void foo() {}
};
这与

bind无关。 由于已经引用的标准@rhalbersma部分,表达&Base::fooDerived的非友元成员中都是非法的,在任何情况下。

但是,如果你的意图是做一些等同于调用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!
}