Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Can't take pointer-to-member of protected member of base class

Reply
Thread Tools

Can't take pointer-to-member of protected member of base class

 
 
K. Frank
Guest
Posts: n/a
 
      03-17-2012
Hello Group!

There is a detail of the protected-member access rules that I don't
understand.
I would expect to be able to take a pointer-to-member of a protected
member
of a base class in a derived class, but I can't.

Here is a sample compilation unit that illustrates the issue:

g++ (4.7.0) gives the following error:

c:/> g++ -c pm_test.cpp
pm_test.cpp: In member function 'virtual void
pm_derived::assign_pm()':
pm_test.cpp:6: error: 'int pm_base::m' is protected
pm_test.cpp:13: error: within this context

Here is the test code:

===== pm_test.cpp =====

class pm_base {
public:
virtual void assign_pm() { pm = &pm_base::m; }

protected: // compiles if m is public
int m; // this is line 6
protected:
int pm_base::*pm;
};

class pm_derived : protected pm_base {
public:
virtual void assign_pm() { pm = &pm_base::m; } // this is line 13
};

==========

(I get a similar error from the Comeau online compiler.)

I would have expected that a derived class would have full access to
the
protected members of the base class, just as it has full access to its
own
protected members. But this interpretation seems not to apply to
taking
a pointer-to-member of a protected member of the base class.

Could someone explain what is going on here? Also, what might be the
rationale for this behavior.

Thanks for any insight.


K. Frank
 
Reply With Quote
 
 
 
 
Victor Bazarov
Guest
Posts: n/a
 
      03-17-2012
On 3/17/2012 11:29 AM, K. Frank wrote:
> Hello Group!
>
> There is a detail of the protected-member access rules that I don't
> understand.
> I would expect to be able to take a pointer-to-member of a protected
> member
> of a base class in a derived class, but I can't.
>
> Here is a sample compilation unit that illustrates the issue:
>
> g++ (4.7.0) gives the following error:
>
> c:/> g++ -c pm_test.cpp
> pm_test.cpp: In member function 'virtual void
> pm_derived::assign_pm()':
> pm_test.cpp:6: error: 'int pm_base::m' is protected
> pm_test.cpp:13: error: within this context
>
> Here is the test code:
>
> ===== pm_test.cpp =====
>
> class pm_base {
> public:
> virtual void assign_pm() { pm =&pm_base::m; }
>
> protected: // compiles if m is public
> int m; // this is line 6
> protected:
> int pm_base::*pm;
> };
>
> class pm_derived : protected pm_base {
> public:
> virtual void assign_pm() { pm =&pm_base::m; } // this is line 13
> };
>
> ==========
>
> (I get a similar error from the Comeau online compiler.)
>
> I would have expected that a derived class would have full access to
> the
> protected members of the base class, just as it has full access to its
> own
> protected members. But this interpretation seems not to apply to
> taking
> a pointer-to-member of a protected member of the base class.
>
> Could someone explain what is going on here? Also, what might be the
> rationale for this behavior.
>
> Thanks for any insight.


Access to protected members of the derived class is only allowed though
the 'this' pointer. A pointer to a member is not "specific" enough (not
restricted to 'this') and therefore is prohibited. It's done to prevent
accessing to base class members of the object that is not of the same
class as '*this'.

V
--
I do not respond to top-posted replies, please don't ask
 
Reply With Quote
 
 
 
 
Richard Damon
Guest
Posts: n/a
 
      03-17-2012
On 3/17/12 5:11 PM, Victor Bazarov wrote:

> Access to protected members of the derived class is only allowed though
> the 'this' pointer. A pointer to a member is not "specific" enough (not
> restricted to 'this') and therefore is prohibited. It's done to prevent
> accessing to base class members of the object that is not of the same
> class as '*this'.
>
> V


I believe it is actually any pointer of the derived type (but not
pointers of the base type). Thus a member function can get to the
protected internals of any object of the derived type that it has access to.
 
Reply With Quote
 
K. Frank
Guest
Posts: n/a
 
      03-18-2012
Hello Victor and Richard!

Thank you for your comments.

On Mar 17, 7:43*pm, Richard Damon <(E-Mail Removed)>
wrote:
> On 3/17/12 5:11 PM, Victor Bazarov wrote:
>
> > Access to protected members of the derived class is only allowed though
> > the 'this' pointer. *A pointer to a member is not "specific" enough (not
> > restricted to 'this') and therefore is prohibited. *It's done to prevent
> > accessing to base class members of the object that is not of the same
> > class as '*this'.

>
> > V

>
> I believe it is actually any pointer of the derived type (but not
> pointers of the base type). Thus a member function can get to the
> protected internals of any object of the derived type that it has access to.


I've mulled this over a little bit, and it sort of makes sense,
but I still don't think I'm looking at it the right way.

Here's an additional aspect that is confusing me: I *am* able to
take a pointer-to-member of a protected member of the base class
if I do it using a pointer-to-member of the derived class. What
I mean by this is shown in the modified sample code:

==========

class pm_base {
public:
virtual void assign_pm() { pm = &pm_base::m; }
protected:
int m;
protected:
int pm_base::*pm;
};

class pm_derived : protected pm_base {
public:
// virtual void assign_pm() { pm = &pm_base::m; } // this line
doesn't compile
virtual void assign_pmd() { pmd = &pm_derived::m; } // this line
does
protected:
int pm_derived::*pmd;
};

==========

This now compiles fine. To emphasize, although in the derived class
I cannot say "pm = &pm_base::m;", I can say "pmd = &pm_derived::m;",
even though in both cases it's the same m -- a protected data member
of the base class -- that I would be getting access to through the
pointer-to-member.

I don't see how to analyze this to see precisely what isn't allowed,
and why not.

Any additional thoughts would be appreciated.

Thanks.


K. Frank
 
Reply With Quote
 
Victor Bazarov
Guest
Posts: n/a
 
      03-18-2012
On 3/17/2012 10:07 PM, K. Frank wrote:
> Hello Victor and Richard!
>
> Thank you for your comments.
>
> On Mar 17, 7:43 pm, Richard Damon<(E-Mail Removed)>
> wrote:
>> On 3/17/12 5:11 PM, Victor Bazarov wrote:
>>
>>> Access to protected members of the derived class is only allowed though
>>> the 'this' pointer. A pointer to a member is not "specific" enough (not
>>> restricted to 'this') and therefore is prohibited. It's done to prevent
>>> accessing to base class members of the object that is not of the same
>>> class as '*this'.

>>
>>> V

>>
>> I believe it is actually any pointer of the derived type (but not
>> pointers of the base type). Thus a member function can get to the
>> protected internals of any object of the derived type that it has access to.

>
> I've mulled this over a little bit, and it sort of makes sense,
> but I still don't think I'm looking at it the right way.
>
> Here's an additional aspect that is confusing me: I *am* able to
> take a pointer-to-member of a protected member of the base class
> if I do it using a pointer-to-member of the derived class. What
> I mean by this is shown in the modified sample code:
>
> ==========
>
> class pm_base {
> public:
> virtual void assign_pm() { pm =&pm_base::m; }
> protected:
> int m;
> protected:
> int pm_base::*pm;
> };
>
> class pm_derived : protected pm_base {
> public:
> // virtual void assign_pm() { pm =&pm_base::m; } // this line
> doesn't compile
> virtual void assign_pmd() { pmd =&pm_derived::m; } // this line
> does
> protected:
> int pm_derived::*pmd;
> };
>
> ==========
>
> This now compiles fine. To emphasize, although in the derived class
> I cannot say "pm =&pm_base::m;", I can say "pmd =&pm_derived::m;",
> even though in both cases it's the same m -- a protected data member
> of the base class -- that I would be getting access to through the
> pointer-to-member.
>
> I don't see how to analyze this to see precisely what isn't allowed,
> and why not.


If you have a pointer-to-member-of-base, you can [try to] access it
using any pointer (or reference) to any derived class of that base, not
just *the* derived class in which you store that pointer. That means
you will be accessing a member of a different branch of the hierarchy,
which isn't allowed. Consider:

class base {
protected:
int a;
};

class derived_one : protected base {
};

class derived_two : protected base {
int base::*member;
derived_two() : member(&base::a) {} // suppose we allow it
void foo(derived_one* other) {
(other->*member) = 42; // here you're changing some other
// object, not yours, not your base
}
};

I think that was the rationale. I am not sure though, perhaps somebody
else can confirm (or refute).

V
--
I do not respond to top-posted replies, please don't ask
 
Reply With Quote
 
K. Frank
Guest
Posts: n/a
 
      03-19-2012
Hi Victor!

Thank you. This makes good sense now.

On Mar 17, 10:22*pm, Victor Bazarov <(E-Mail Removed)> wrote:
> On 3/17/2012 10:07 PM, K. Frank wrote:
> ...
> > This now compiles fine. *To emphasize, although in the derived class
> > I cannot say "pm =&pm_base::m;", I can say "pmd =&pm_derived::m;",
> > even though in both cases it's the same m -- a protected data member
> > of the base class -- that I would be getting access to through the
> > pointer-to-member.

>
> > I don't see how to analyze this to see precisely what isn't allowed,
> > and why not.

>
> If you have a pointer-to-member-of-base, you can [try to] access it
> using any pointer (or reference) to any derived class of that base, not
> just *the* derived class in which you store that pointer. *That means
> you will be accessing a member of a different branch of the hierarchy,
> which isn't allowed.


Ah, okay. This all hangs together now.

> Consider:
>
> * * *class base {
> * * *protected:
> * * * * int a;
> * * *};
>
> * * *class derived_one : protected base {
> * * *};
>
> * * *class derived_two : protected base {
> * * * * int base::*member;
> * * * * derived_two() : member(&base::a) {} // suppose we allow it
> * * * * void foo(derived_one* other) {
> * * * * * *(other->*member) = 42; // here you're changing some other
> * * * * * * * * * * * * * * * * * // object, not yours, not your base
> * * * * }
> * * *};
>
> I think that was the rationale. *I am not sure though, perhaps somebody
> else can confirm (or refute).


Yes, this would seem to be the likely explanation.

>
> V


Thank you again.

(And now, just to confuse myself further, I'm looking at how
pointer-to-member-functions work when the member functions are
virtual and are overridden.)


K. Frank
 
Reply With Quote
 
Krice
Guest
Posts: n/a
 
      03-19-2012
On 18 maalis, 04:07, "K. Frank" <(E-Mail Removed)> wrote:
> This now compiles fine.


Many things can compile fine in C++ but still fail.
Pointers have a different task in C++ than they have in C,
and when you try to think why something pointer related
wont work in C++ you are already sidetracked.
 
Reply With Quote
 
Alf P. Steinbach
Guest
Posts: n/a
 
      03-20-2012
On 17.03.2012 16:29, K. Frank wrote:
>
> g++ (4.7.0) gives the following error:
>
> c:/> g++ -c pm_test.cpp
> pm_test.cpp: In member function 'virtual void
> pm_derived::assign_pm()':
> pm_test.cpp:6: error: 'int pm_base::m' is protected
> pm_test.cpp:13: error: within this context
>
> Here is the test code:
>
> ===== pm_test.cpp =====
>
> class pm_base {
> public:
> virtual void assign_pm() { pm =&pm_base::m; }
>
> protected: // compiles if m is public
> int m; // this is line 6
> protected:
> int pm_base::*pm;
> };
>
> class pm_derived : protected pm_base {
> public:
> virtual void assign_pm() { pm =&pm_base::m; } // this is line 13
> };
>
> ==========
>
> (I get a similar error from the Comeau online compiler.)
>
> I would have expected that a derived class would have full access to
> the protected members of the base class, just as it has full access to its
> own protected members. But this interpretation seems not to apply to
> taking a pointer-to-member of a protected member of the base class.


An instance of a derived class does have full access to the public and
protected members of any accessible base class part of itself.

The ideal, to prevent "hacks" of classes by deriving from some common
base, would be that an instance of a derived class could not go beyond
the access outlined above, e.g. that it could not access protected parts
of a Base& (even though it can access such parts of itself or other
instances of its own class or some derived class).

For example,


<code>
class Base
{
protected:
int b_;
};

class Derived
: public Base
{
public:
static int& bOfDerived( Derived& derivedObject )
{
return derivedObject.b_; // OK.
}

static int& bOfBase( Base& baseObject )
{
return baseObject.b_; // !Nyet
}
};

int main() {}
</code>


Self test: if the rule above didn't exist, how could you easily
/inadvertently/ access parts of someone else's class, when it shares
some common base with your class?

This rule does not prevent intentional hacking. It is only about
inadvertent (and then usually disastrous) access. As example, let's
intentionally hack into the protected member "c" of a "std::stack", by
just a little friendly static_cast'ing.

Heads-up: as you will see below (if you read on), member pointers
provide a loophole -- they're very low-level, sort of like "goto" --
where it is possible to do this without any casting, and indeed
without any conversion!


<code>
#include <iostream> // std::wcout, std::endl
#include <stack> // std::stack
#include <utility> // std::begin, std::end
using namespace std;

typedef stack<int> Stack;
typedef Stack::container_type Container;

Container const& containerOf( Stack const& st )
{
struct Hack: Stack
{
static Container const& container( Stack const& st )
{
return static_cast<Hack const&>( st ).c;
}
};

return Hack::container( st );
}

void display( Stack const& st )
{
Container const& c = containerOf( st );
for( auto it = begin( c ); it != end( c ); ++it )
{
wcout << *it << ' ';
}
wcout << endl;
}

Stack& pushedOn( Stack& st, int const v )
{
st.push( v );
return st;
}

Stack& operator<<( Stack&& st, int const v )
{ return pushedOn( st, v ); }

Stack& operator<<( Stack& st, int const v )
{ return pushedOn( st, v ); }

int main()
{
Stack const st = move( Stack() << 3 << 1 << 4 << 1 << 5 );

display( st );
display( st );
display( st );
}
</code>


The usual question prompting an explanation such as above, is how Base
(or rather, the questioner) can provide a protected Base setter method
to Derived classes, so that they can apply that setter on Base& objects?

Well, there is a difference between accessing a protected something in
an /instance/ of Base, versus accessing a protected static something
inherited directly from Base.

In the latter case, with a sound design there is no chance of
inadvertently changing something in an instance of someone else's
derived class, and also it does not open up for hacking:


<code>
#include <iostream>
using namespace std;

class Base
{
protected:
int b_;

void setB( int const v ) { b_ = v; }
static void setB( Base& o, int const v ) { o.setB( v ); }
};

class Derived
: public Base
{
public:
static void foo( Base& o )
{
setB( o, 42 ); // Static member, OK.
o.setB( 666 ); // Instance member, !Nyet.
}
};

int main() {}
</code>


The situation with ordinary pointers is the same.

However, member pointers are not ordinary pointers. They're not
addresses. They're more like what used to be called /based pointers/,
i.e. offsets -- the simplest member pointer represents an offset from
the start of an instance of the class.

It can be a bit more complicated that, for pointers to virtual methods,
and when virtual inheritance is employed, but that's the basic idea, the
basic conceptual picture.

Now, an offset to some place in a Base instance can clearly be applied
to a Derived instance and mean the same there - at least in the simplest
case, and the more complicated cases are just complicated because the
internals have to support this simple programmers' picture.

However, an offset to some place in a Derived instance can not in
general be applied to a Base instance, because generally a Base instance
need not have anything there. For it can be the offset of some member
that is introduced down in Derived. So, summing up so far, a "T
(Base::*)" or "T (Base::*)(args)" can be applied to a Derived object,
but a "T (Derived::*)" or "T (Derived::*)(args)" can not in general be
applied to Base object.

And these are exactly the restrictions that the C++ rules place on
member pointers, so that the earlier hack can be rewritten as ...


<code>
Container const& containerOf( Stack const& st )
{
struct Hack: Stack
{
static Container const& container( Stack const& st )
{
return st.*&Hack::c;
}
};

return Hack::container( st );
}
</code>


which does not involve any casting or conversions, he he.

The direct reason that this works is that

`&Hack::c` is of -- ta da! -- type `Container (Stack::*)`

because c is inherited from Stack.

Yes, believe it or not, the type of the result of the address operator
here depends on whether the argument is inherited:


<code>
#include <iostream>
#include <typeinfo>
using namespace std;

struct Base { int b; };
struct Derived: Base { double d; };

void show( char const expression[], char const value[] )
{
wcout << expression << " -> " << value << endl;
}

#define SHOW( e ) show( #e, typeid( e ).name() )

int main()
{
SHOW( &Base::b );
SHOW( &Derived::b );
SHOW( &Derived::d );
}
</code>

<output>
&Base::b -> int Base::*
&Derived::b -> int Base::*
&Derived::d -> double Derived::*
</output>


Now, as you can see this stuff is a little complicated and
counter-intuitive, well, maybe more than just a little complicated, and
as a logical consequence of the general behavior member pointers allow
you to /inadvertently/ circumvent the normal access rules, as shown in
the Stack hack.

But this means that I don't know the answer to your question.

For the member pointer your derived class is not allowed to obtain
directly, where it cannot do &Base:, it can obtain nearly as directly
via &Derived:. And very painlessly. So I think it's just a "language
wart", an undesirable but not very serious consequence of the rules, not
worth special casing to remove.


Cheers & hth.,

- Alf
 
Reply With Quote
 
K. Frank
Guest
Posts: n/a
 
      03-22-2012
Hello Alf!

Thank you. This is food for thought and very helpful.

On Mar 20, 7:08*pm, "Alf P. Steinbach" <alf.p.steinbach
(E-Mail Removed)> wrote:
> On 17.03.2012 16:29, K. Frank wrote:
> ...
> > pm_test.cpp:6: error: 'int pm_base::m' is protected
> > pm_test.cpp:13: error: within this context

> ...
> > * *protected: *// compiles if m is public
> > * * *int m; *// this is line 6
> > ...
> > * * *virtual void assign_pm() { pm =&pm_base::m; } *// this is line 13
> > ...

>
> An instance of a derived class does have full access to the public and
> protected members of any accessible base class part of itself.
>
> The ideal, to prevent "hacks"...
> ...
> This rule does not prevent intentional hacking. It is only about
> inadvertent (and then usually disastrous) access. As example, let's
> intentionally hack into the protected member "c" of a "std::stack", by
> just a little friendly static_cast'ing.
> ...
> <code>
> * * *#include <iostream> * * * * // std::wcout, std::endl
> * * *#include <stack> * * * * * *// std::stack
> * * *#include <utility> * * * * *// std::begin, std::end
> * * *using namespace std;
>
> * * *typedef stack<int> * * * * * * *Stack;
> * * *typedef Stack::container_type * Container;
>
> * * *Container const& containerOf( Stack const& st )
> * * *{
> * * * * *struct Hack: Stack
> * * * * *{
> * * * * * * *static Container const& container( Stack const& st )
> * * * * * * *{
> * * * * * * * * *return static_cast<Hack const&>( st ).c;
> * * * * * * *}
> * * * * *};


I expect that this hack would work on most (all?) implementations.
But isn't it the case -- please correct me if I'm wrong -- that if
st, the argument to the static cast, refers only to a Stack, but
not, polymorphically, to an actual Hack, then the static cast to
Hack& yields technically undefined behavior?

>
> * * * * *return Hack::container( st );
> * * *}
> ...
> ...
> For the member pointer your derived class is not allowed to obtain
> directly, where it cannot do &Base:, it can obtain nearly as directly
> via &Derived:.


Yes, this makes sense. As I noted earlier in the thread,

:: I *am* able to
:: take a pointer-to-member of a protected member of the base
class
:: if I do it using a pointer-to-member of the derived class.

:: // this line doesn't compile
:: // virtual void assign_pm() { pm = &pm_base::m; }
:: // this line does
:: virtual void assign_pmd() { pmd = &pm_derived::m; }


> And very painlessly. So I think it's just a "language
> wart", an undesirable but not very serious consequence of the rules, not
> worth special casing to remove.


I am leaning towards the belief that this is a legitimate and
natural consequence (even if somewhat complicated and counter-
intuitive) of how the various rules interact, rather than an
oversight or flaw in the design. But I'm really not sure.
Perhaps there could have been a cleaner design in this corner
of the language.

> Cheers & hth.,
>
> - Alf


Thanks again for your explanations and detailed examples.


K. Frank
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Can a sub-class (composite object) member function access private or protected members of base class? pkpatil@gmail.com C++ 2 06-09-2006 03:09 PM
When is a base class protected member not visible in a derived class? Andy Lomax C++ 5 06-30-2005 04:34 AM
Can Derived class static member access protected member from base class? Siemel Naran C++ 4 01-12-2005 06:46 PM
Is this right? Can't call protected member of base class from derivedclass method, for another object Asfand Yar Qazi C++ 17 09-12-2003 04:44 PM
Is this valid C++ (protected member of base accessed in derived class function) Shelly Adhikari C++ 3 09-10-2003 08:18 PM



Advertisments