问题
I want to bind()
to my base class's version of a function from the derived class. The function is marked protected in the base. When I do so, the code compiles happily in Clang (Apple LLVM Compiler 4.1) but gives an error in both g++ 4.7.2 and in Visual Studio 2010. The error is along the lines of: "'Base::foo' : cannot access protected member."
The implication is that the context for the reference is actually within bind()
, where of course the function is seen as protected. But shouldn't bind()
inherit the context of the calling function--in this case, Derived::foo()
--and therefore see the base method as accessible?
The following program illustrates the issue.
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 );
}
};
Why the discrepancy in behavior? Which is correct? What workaround is available for the error-giving compilers?
回答1:
Answer: see boost::bind with protected members & context which quotes this part of the Standard
An additional access check beyond those described earlier in clause 11 is applied when a non-static data member or nonstatic member function is a protected member of its naming class (11.2)105) As described earlier, access to a protected member is granted because the reference occurs in a friend or member of some class C. If the access is to form a pointer to member (5.3.1), the nested-name-specifier shall name C or a class derived from C. All other accesses involve a (possibly implicit) object expression (5.2.5). In this case, the class of the object expression shall be C or a class derived from C.
Workaround: make foo
a public
member function
#include <functional>
struct Base
{
public: virtual void foo() {}
};
回答2:
This has nothing to do with bind
. Because of the piece of the Standard @rhalbersma already quoted, the expression &Base::foo
is illegal in a non-friended member of Derived
, in every context.
But if your intent was to do something equivalent to calling Base::foo();
, you have a bigger issue: pointers to member functions always invoke a virtual override.
#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!
}
来源:https://stackoverflow.com/questions/14509291/stdbind-ing-a-base-protected-member-function-from-a-derived-classs-member-f