I'd like to std::bind
to a member function from a private base class, made "public" with a using
-declaration in the derived class. Calling the function directly works, but it seems binding or using member function pointers doesn't compile:
#include <functional>
struct Base {
void foo() { }
};
struct Derived : private Base {
using Base::foo;
};
int main(int, char **)
{
Derived d;
// call member function directly:
// compiles fine
d.foo();
// call function object bound to member function:
// no matching function for call to object of type '__bind<void (Base::*)(), Derived &>'
std::bind(&Derived::foo, d)();
// call via pointer to member function:
// cannot cast 'Derived' to its private base class 'Base'
(d.*(&Derived::foo))();
return 0;
}
Looking at the error messages above, the issue seems to be that Derived::foo
is still just Base::foo
, and I can't access Base
through Derived
outside Derived
itself.
This seems inconsistent - should I not be able to use direct calls, bound functions, and function pointers interchangeably?
Is there a workaround that would let me bind to foo
on a Derived
object, preferably without changing Base
or Derived
(which are in a library I don't own)?
The issue here is what the using-declaration actually does:
struct Derived : private Base {
using Base::foo;
};
That brings Base::foo
into public scope in Derived
, but it doesn't create an entirely new function. It is not equivalent to having written:
struct Derived : private Base {
void foo() { Base::foo(); }
}
There is still only Base::foo()
. The using-declaration simply affects the access rules and the overload resolution rules. As such &Derived::foo
really has type void (Base::*)()
(and not void (Derived::*)()
!), since that is the only foo
that exists. Since Base
is private
, member access through a pointer to Base
is ill-formed. I agree that this is pretty unfortunate ("inconsistent" is a good word).
You can still create a function object that calls foo
. You just can't use the pointer to member. With C++14, this becomes straightforward if verbose (I'm assuming arbitrary arguments here and that void foo()
is merely a simplification of the problem):
auto d_foo = [d](auto&&... args){ return d.foo(std::forward<decltype(args)>(args)...); }
With C++11, you'd have to write a type with a variadic template operator()
.