我希望从派生类中bind()
到我的基类的函数版本。该功能在基础中标记为受保护。当我这样做时,代码在Clang(Apple LLVM编译器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 );
}
};
为什么行为上存在差异?哪个是对的?错误提供编译器有哪些可用的解决方法?
答案 0 :(得分: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!
}
答案 1 :(得分:9)
答案:请参阅引用标准
部分的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() {}
};