Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does using declaration not expose pointer to member

Consider:

struct foo
{
    void foobar(){}
};

struct bar : protected foo
{
    using foo::foobar;
};

int main()
{
    bar b;
    b.foobar(); // Fine
    &bar::foobar; // Not fine
}

I'm wondering what the rationale for letting using declarations expose the member, but not a pointer to it. In fact it would seem all using declarations that changes access level works for everything except taking the address of an exposed function.

UPDATE: An example which resembles my real use case better:

#include "boost/bind.hpp"

struct foo
{
    void foobar() {}
};

struct bar : protected foo
{
    using foo::foobar;
    bar() { boost::bind( &bar::foobar, this )(); } // Crashes VS2008, GCC 4.1.1 fails to compile as it tries to go through foo*
};

int main()
{
    bar b;
}

However, Mike Seymours' explanation is spot on and explains why GCC fails. Thanks!

like image 293
Ylisar Avatar asked May 26 '26 05:05

Ylisar


1 Answers

[I am assuming that in your program the code is: void (bar::*p)() = &bar::foobar;]

The problem is not that the using declaration does not bring the identifier into space, but the semantics of &bar::foobar. I am considering (I would have done it if I had the time) refilling a Defect Report with this. There is already one such report.

Basically the problem is that the using declaration brings the base function into scope for lookup in the derived type, and the access specifiers for the expression &bar::foobar will be checked against bar. But, the result of the expression &bar::foobar is of type void (foo::*)(), not void (bar::*)(). Now, after evaluation of &bar::foobar if you try to use that as a void (bar::*)() the compiler will try to perform the conversion of the pointer to member but will fail because foo is a protected base of bar, and in the context of main you don't have access to that relationship.

Note that I consider that to be a defect in the language for two reasons: first it breaks your code: void (bar::*p)() = &bar::foobar; surprisingly fails to compile. Secondly, it breaks access protections in other cases:

class base {
protected: void f() {}
};
struct derived : base {
   void foo( base& b ) {
       b.f();                // Error
       b.*(&derived::f)();   // OK
   }
};

This problem is actually symmetric to yours, while in yours the surprising type of the address-of-member operation inhibits your use case when it shouldn't, in this case it allows an usage that is against the intent of protected.

Related links:

  • DR 203 - Type of address-of-member expression
  • DR 1007 - Protected access and pointers to members

After the comment on using bind, it might not be the case that you are trying to convert the pointer to member to a pointer to member of bar directly, but somewhere inside bind code will be generated to apply the pointer to member to an instance of bar, and that requires the conversion.

like image 137
David Rodríguez - dribeas Avatar answered May 27 '26 19:05

David Rodríguez - dribeas



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!