Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

C2248: cannot access protected member

29 views
Skip to first unread message

Alex Blekhman

unread,
Jan 31, 2008, 1:49:26 PM1/31/08
to
Hello,

I tried following code with VC++2005, VC++2008 and Comeau C/C++:

<code>

class X
{
protected:
void foo() {}
int a;
};

class Y : public X
{
public:
void bar()
{
X::foo(); // OK
int b = X::a; // OK

void (X::*pf)() = &X::foo; // error C2248!
int X::*pa = &X::a; // error C2248!
}
};

</code>

All compilers emit C2248 error or equivalent. I want to understand
why this happens. Why accessing protected members is OK while
taking their pointers is an error?

Thanks
Alex


Alex Blekhman

unread,
Jan 31, 2008, 1:57:01 PM1/31/08
to
I found following bug description, which suggests that C2248 error
is a bag:

"VC8 SP1: C++/CLI: taking address of protected bound-member"
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=260265

However the fact that Comeau C/C++ complines, too make me doubt
that.

Alex


Alf P. Steinbach

unread,
Jan 31, 2008, 2:10:22 PM1/31/08
to
* Alex Blekhman:

struct Base
{
protected:
int myCriticalThing;
public:
// Stuff.
};

struct SomeonesDerived: Base
{
// Stuff
};

struct AlexHack: Base
{
static void hackIt( Base& o )
{
o.myCriticalThing = 666; // Happily, not allowed in C++.
}
};

int main()
{
SomeonesDerived o;
AlexHack::hackIt( o ); // Could, if not for restriction.
}


Cheers, & hth.,

- Alf (just visiting due to George's articles over in clc++ :-))

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

Igor Tandetnik

unread,
Jan 31, 2008, 2:14:15 PM1/31/08
to
Alex Blekhman <tkfx....@yahoo.com> wrote:
> class X
> {
> protected:
> void foo() {}
> int a;
> };
>
> class Y : public X
> {
> public:
> void bar()
> {
> X::foo(); // OK
> int b = X::a; // OK
>
> void (X::*pf)() = &X::foo; // error C2248!
> int X::*pa = &X::a; // error C2248!
> }
> };
>
> All compilers emit C2248 error or equivalent. I want to understand
> why this happens. Why accessing protected members is OK while
> taking their pointers is an error?

The short answer is that the C++ standard says so:

11.5/1 When a friend or a member function of a derived class references
a protected nonstatic member of a base class, an access check applies in
addition to those described earlier in clause 11. Except when forming a
pointer to member (5.3.1), the access must be through a pointer to,
reference to, or object of the derived class itself (or any class
derived from that class) (5.2.5). If the access is to form a pointer to
member, the nested-name-specifier shall name the derived class (or any
class derived from that class).

So &X::foo fails the access check, but &Y::foo works.

Here's the long answer. Class Y is presumed to know how it uses
protected members of its base class X, and can access them freely. But
it doesn't necessarily know how some other class Z derived from X might
use X's protected members, and is not allowed to mess up with those.
Consider:

class Z : public X {};

void Y::baz(Z* pz) {
X::foo(); // fine - calls foo() on itself
pz->X::foo(); // doesn't compile - calls foo on Z

int b = X::a; // fine - access its own members
b = pz->X::a; // doesn't compile
}

If &X::foo worked inside Y, baz() could have worked around the
restrictions like this:

void Y::baz(Z* pz) {
void (X::*pf)() = &X::foo; // hypothetical, doesn't compile
(pz->*pf)(); // same as pz->foo()
}

--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not
necessarily a good idea. It is hard to be sure where they are going to
land, and it could be dangerous sitting under them as they fly
overhead. -- RFC 1925


Ben Voigt [C++ MVP]

unread,
Jan 31, 2008, 2:25:09 PM1/31/08
to

"Alex Blekhman" <tkfx....@yahoo.com> wrote in message
news:O7yCCoDZ...@TK2MSFTNGP02.phx.gbl...

Because this doesn't work either:

class Y : public X
{
public:
void bar()
{

X x;
x.foo(); // fails, Y::bar can access protected X::foo only
through a Y instance
}
};

This should work (I didn't test it):

void (Y::*pf)() = &X::foo;
int Y::*pa = &X::a; // error C2248!

>
> Thanks
> Alex
>


Alex Blekhman

unread,
Jan 31, 2008, 3:26:27 PM1/31/08
to
"Ben Voigt [C++ MVP]" wrote:
> This should work (I didn't test it):
>
> void (Y::*pf)() = &X::foo;
> int Y::*pa = &X::a; // error C2248!

I think you mean:

void (X::*pf)() = &Y::foo;


Alex


Alex Blekhman

unread,
Jan 31, 2008, 3:37:30 PM1/31/08
to
"Igor Tandetnik" wrote:
> Here's the long answer. [...]

Thanks for the elaborate answer. Now it makes sense. I don't
remember ever encountering this dusty corner of the language.

What I tried to do is to find acceptable workaround for the
problem from "How are virtual function calls implemented?" thread
started by Anthony Wieser. Basically, the code looks like this:

class MfcX
{
protected:
virtual void foo() {}
};

class UserY : public MfcX
{
public:
virtual void foo() {}

void bar()
{
// will crash due to bug in VC+2005 SP1
//MfcX::foo();

// nice attempt
void (MfcX::*pf)() = &MfcX::foo;
MfcX* pParent = this;
(pParent->*pf)();
}
};

Now, the above attempt won't compile. Making it using `UserY' name
won't work either:

// another nice attempt
void (UserY::*pf)() = &UserY::foo;
MfcX* pParent = this;
(pParent->*pf)(); // won't compile either

That's about it.

Alex


Alex Blekhman

unread,
Jan 31, 2008, 3:39:51 PM1/31/08
to
"Alf P. Steinbach" wrote:
> [...]

> - Alf (just visiting due to George's articles over in clc++
> :-))

Visit more often. :) Thanks for the answer.

Alex


Igor Tandetnik

unread,
Jan 31, 2008, 3:43:14 PM1/31/08
to

If I could do _that_, I would be able to call a method on an object that
doesn't have this method in the first place:

class Base {};

class Derived : public Base {
public:
void foo();
};

void (Base::*pf)() = &Derived::foo;
Base b;
(b.*pf)(); // b.foo() ????

I guess both of you meant

void (Y::*pf)() = &Y::foo;

Doug Harrison [MVP]

unread,
Jan 31, 2008, 4:03:17 PM1/31/08
to
On Thu, 31 Jan 2008 15:43:14 -0500, "Igor Tandetnik" <itand...@mvps.org>
wrote:

>Alex Blekhman <tkfx....@yahoo.com> wrote:
>> "Ben Voigt [C++ MVP]" wrote:
>>> This should work (I didn't test it):
>>>
>>> void (Y::*pf)() = &X::foo;
>>> int Y::*pa = &X::a; // error C2248!
>>
>> I think you mean:
>>
>> void (X::*pf)() = &Y::foo;
>
>If I could do _that_, I would be able to call a method on an object that
>doesn't have this method in the first place:

Yes, the implicit cast between derived->base sort of reverses into
base->derived for pointers to members. At least that's how I remember it.

--
Doug Harrison
Visual C++ MVP

Ben Voigt [C++ MVP]

unread,
Jan 31, 2008, 4:22:19 PM1/31/08
to

"Igor Tandetnik" <itand...@mvps.org> wrote in message
news:OUIS1rEZ...@TK2MSFTNGP06.phx.gbl...

> Alex Blekhman <tkfx....@yahoo.com> wrote:
>> "Ben Voigt [C++ MVP]" wrote:
>>> This should work (I didn't test it):
>>>
>>> void (Y::*pf)() = &X::foo;
>>> int Y::*pa = &X::a; // error C2248!
>>
>> I think you mean:
>>
>> void (X::*pf)() = &Y::foo;
>
> If I could do _that_, I would be able to call a method on an object that
> doesn't have this method in the first place:
>
> class Base {};
>
> class Derived : public Base {
> public:
> void foo();
> };
>
> void (Base::*pf)() = &Derived::foo;
> Base b;
> (b.*pf)(); // b.foo() ????
>
>
>
> I guess both of you meant
>
> void (Y::*pf)() = &Y::foo;

No, I meant what I wrote

void (Y::*pf)() = &X::foo;

which is a fully-bound (non-virtual) member function, calling the exact
function X::foo on any instance of Y.

I don't know if the C++ compiler will accept that syntax, it should because
it is type safe. if Y is a subtype of X, then Y -> unit is a supertype of
X -> unit.

Y can access the base implementation of protected member foo, so it should
be able to create a pointer-to-member that does so.

Ben Voigt [C++ MVP]

unread,
Jan 31, 2008, 4:27:06 PM1/31/08
to

"Ben Voigt [C++ MVP]" <r...@nospam.nospam> wrote in message
news:uBluN8EZ...@TK2MSFTNGP02.phx.gbl...

>
> "Igor Tandetnik" <itand...@mvps.org> wrote in message
> news:OUIS1rEZ...@TK2MSFTNGP06.phx.gbl...
>> Alex Blekhman <tkfx....@yahoo.com> wrote:
>>> "Ben Voigt [C++ MVP]" wrote:
>>>> This should work (I didn't test it):
>>>>
>>>> void (Y::*pf)() = &X::foo;
>>>> int Y::*pa = &X::a; // error C2248!
>>>
>>> I think you mean:
>>>
>>> void (X::*pf)() = &Y::foo;
>>
>> If I could do _that_, I would be able to call a method on an object that
>> doesn't have this method in the first place:
>>
>> class Base {};
>>
>> class Derived : public Base {
>> public:
>> void foo();
>> };
>>
>> void (Base::*pf)() = &Derived::foo;
>> Base b;
>> (b.*pf)(); // b.foo() ????
>>
>>
>>
>> I guess both of you meant
>>
>> void (Y::*pf)() = &Y::foo;
>
> No, I meant what I wrote
>
> void (Y::*pf)() = &X::foo;
>
> which is a fully-bound (non-virtual) member function, calling the exact
not member function but pointer-to-member-function

Alex Blekhman

unread,
Jan 31, 2008, 4:26:42 PM1/31/08
to
"Igor Tandetnik" wrote:
>> I think you mean:
>>
>> void (X::*pf)() = &Y::foo;
>
> If I could do _that_, I would be able to call a method on an
> object that doesn't have this method in the first place:
>
> class Base {};
>
> class Derived : public Base {
> public:
> void foo();
> };
>
> void (Base::*pf)() = &Derived::foo;
> Base b;
> (b.*pf)(); // b.foo() ????
>
>
>
> I guess both of you meant
>
> void (Y::*pf)() = &Y::foo;

Obviously the "void (Y::*pf)() = &Y::foo;" statement will work.
However, I meant

void (X::*pf)() = &Y::foo;

statement. If `X' actualy contains `foo' member, then everything
compiles happily. If `foo' exists only in `Y', then you'll get
compilation error:

C2440: cannot convert from 'void (__thiscall Y::* )(void)' to
'void (__thiscall X::* )(void)'

Alex


Igor Tandetnik

unread,
Jan 31, 2008, 4:29:38 PM1/31/08
to
Ben Voigt [C++ MVP] <r...@nospam.nospam> wrote:
> "Igor Tandetnik" <itand...@mvps.org> wrote in message
> news:OUIS1rEZ...@TK2MSFTNGP06.phx.gbl...
>> I guess both of you meant
>>
>> void (Y::*pf)() = &Y::foo;
>
> No, I meant what I wrote
>
> void (Y::*pf)() = &X::foo;

That still shouldn't compile, due to access check on X::foo, as
described in C++ standard 11.5/1. See my earlier answer in this thread.

> I don't know if the C++ compiler will accept that syntax, it should
> because it is type safe.

The problem has nothing to do with type safety, but with access control.

> Y can access the base implementation of protected member foo, so it
> should be able to create a pointer-to-member that does so.

If it were able to do so, it would be able to call foo on some other
class Z derived from X. But it shouldn't be able to call a protected
method on an unrelated class, even if the two share the same base.

Doug Harrison [MVP]

unread,
Jan 31, 2008, 4:29:16 PM1/31/08
to
On Thu, 31 Jan 2008 14:14:15 -0500, "Igor Tandetnik" <itand...@mvps.org>
wrote:

>The short answer is that the C++ standard says so:

I was thinking the same thing, but it doesn't seem like the compiler would
let you say the following even if the hypothetical initialization worked:

(pz->*pf)();

for the same reason it doesn't allow:

pz->foo();

However, it would let you say:

(this->*pf)();

which is potentially useful in a limited way. Am I missing something?

Alex Blekhman

unread,
Jan 31, 2008, 4:33:12 PM1/31/08
to
"Ben Voigt [C++ MVP]" wrote:
> No, I meant what I wrote
>
> void (Y::*pf)() = &X::foo;
>
> which is a fully-bound (non-virtual) member function, calling
> the exact function X::foo on any instance of Y.
>
> I don't know if the C++ compiler will accept that syntax, it
> should because it is type safe. if Y is a subtype of X, then
> Y -> unit is a supertype of X -> unit.
>
> Y can access the base implementation of protected member foo, so
> it should be able to create a pointer-to-member that does so.

Actually, I started this thread because the C++ compiler doesn't
accept that syntax. Igor cited relevant part of the Standard that
explicitly forbids it and demonstrated with short example why it
forbids such sintax.

Alex


Ben Voigt [C++ MVP]

unread,
Jan 31, 2008, 4:47:22 PM1/31/08
to

"Doug Harrison [MVP]" <d...@mvps.org> wrote in message
news:3td4q35lnijrv5top...@4ax.com...

Of course it would, pf was declared as a pointer-to-member of any X

>
> for the same reason it doesn't allow:
>
> pz->foo();

That's why the initialization fails, because baz isn't allowed to call foo
on any X, which is part of the type for pf.

Ben Voigt [C++ MVP]

unread,
Jan 31, 2008, 4:48:38 PM1/31/08
to

"Igor Tandetnik" <itand...@mvps.org> wrote in message
news:Oy5MjBFZ...@TK2MSFTNGP03.phx.gbl...

> Ben Voigt [C++ MVP] <r...@nospam.nospam> wrote:
>> "Igor Tandetnik" <itand...@mvps.org> wrote in message
>> news:OUIS1rEZ...@TK2MSFTNGP06.phx.gbl...
>>> I guess both of you meant
>>>
>>> void (Y::*pf)() = &Y::foo;
>>
>> No, I meant what I wrote
>>
>> void (Y::*pf)() = &X::foo;
>
> That still shouldn't compile, due to access check on X::foo, as described
> in C++ standard 11.5/1. See my earlier answer in this thread.
>
>> I don't know if the C++ compiler will accept that syntax, it should
>> because it is type safe.
>
> The problem has nothing to do with type safety, but with access control.
>
>> Y can access the base implementation of protected member foo, so it
>> should be able to create a pointer-to-member that does so.
>
> If it were able to do so, it would be able to call foo on some other class
> Z derived from X. But it shouldn't be able to call a protected method on
> an unrelated class, even if the two share the same base.

How? I declared pf in such a way that it can only be used on Y instances,
not X or any class derived from X but not Y, such as your Z. What is the
problem?

Ben Voigt [C++ MVP]

unread,
Jan 31, 2008, 4:53:53 PM1/31/08
to

"Alex Blekhman" <tkfx....@yahoo.com> wrote in message
news:ex7dUDFZ...@TK2MSFTNGP03.phx.gbl...

Igor's argument applies to

void (X::*pf)() = &X::foo; // inside Y::bar

not what I wrote which is

void (Y::*pf)() = &X::foo; // inside Y::bar

My version, which may or may not be accepted by the compiler, is perfectly
typesafe.

Of course the following will certainly work and provide an identical result:

class Y : X
{
void fooshim() { X::foo(); }
public:
void foo() override;
void bar()
{
void (Y::*pf)() = &Y::fooshim;
}
};

>
> Alex
>


Igor Tandetnik

unread,
Jan 31, 2008, 4:51:44 PM1/31/08
to
Doug Harrison [MVP] <d...@mvps.org> wrote:
> I was thinking the same thing, but it doesn't seem like the compiler
> would let you say the following even if the hypothetical
> initialization worked:
>
> (pz->*pf)();
>
> for the same reason it doesn't allow:
>
> pz->foo();

Why? The compiler doesn't know that pf is bound to foo. It could be
bound to some public method xyz() of X, and pz->xyz() is perfectly fine.

For example, it is legal for a class to return a pointer to its own
private method, and let the outside client call it:

class C {
void f();
public:
typedef void (C::PF)();
PF get() { return &C::f; }
};

C c;
C::PF pf = c.get();
(c*.pf)(); // c.f();

Igor Tandetnik

unread,
Jan 31, 2008, 4:55:47 PM1/31/08
to
Ben Voigt [C++ MVP] <r...@nospam.nospam> wrote:
> "Igor Tandetnik" <itand...@mvps.org> wrote in message
> news:Oy5MjBFZ...@TK2MSFTNGP03.phx.gbl...

>>> No, I meant what I wrote
>>>
>>> void (Y::*pf)() = &X::foo;
>>
>> If it were able to do so, it would be able to call foo on some other
>> class Z derived from X. But it shouldn't be able to call a protected
>> method on an unrelated class, even if the two share the same base.
>
> How? I declared pf in such a way that it can only be used on Y
> instances, not X or any class derived from X but not Y, such as your
> Z. What is the problem?

There's no access control check on assignment. As soon as &X::foo is a
valid expression, you can assign it to a variable of type void (X::*)()
without any further checks. So the compiler performs access control on
address-of, and disallows &X::foo inside Y.

Igor Tandetnik

unread,
Jan 31, 2008, 5:01:32 PM1/31/08
to
Ben Voigt [C++ MVP] <r...@nospam.nospam> wrote:
> "Alex Blekhman" <tkfx....@yahoo.com> wrote in message
> news:ex7dUDFZ...@TK2MSFTNGP03.phx.gbl...
>> Actually, I started this thread because the C++ compiler doesn't
>> accept that syntax. Igor cited relevant part of the Standard that
>> explicitly forbids it and demonstrated with short example why it
>> forbids such sintax.
>
> Igor's argument applies to
>
> void (X::*pf)() = &X::foo; // inside Y::bar
>
> not what I wrote which is
>
> void (Y::*pf)() = &X::foo; // inside Y::bar

Again, the compiler won't even get to the assignment part. Just writing
a no-op like

&X::foo;

will already fail to compile.

> My version, which may or may not be accepted by the compiler, is
> perfectly typesafe.

Again, nobody doubts the type safety. It's access control that's
blocking your code.

Igor Tandetnik

unread,
Jan 31, 2008, 5:04:21 PM1/31/08
to
Alex Blekhman <tkfx....@yahoo.com> wrote:
> Obviously the "void (Y::*pf)() = &Y::foo;" statement will work.
> However, I meant
>
> void (X::*pf)() = &Y::foo;
>
> statement. If `X' actualy contains `foo' member, then everything
> compiles happily. If `foo' exists only in `Y', then you'll get
> compilation error:
>
> C2440: cannot convert from 'void (__thiscall Y::* )(void)' to
> 'void (__thiscall X::* )(void)'

This statement should fail regardless of whether X::foo exists or not.
It's a type mismatch - the actual values are irrelevant. If it actually
compiles under some circumstances, I believe it's a compiler bug.

Ben Voigt [C++ MVP]

unread,
Jan 31, 2008, 6:15:35 PM1/31/08
to

"Igor Tandetnik" <itand...@mvps.org> wrote in message
news:%23WcJKQF...@TK2MSFTNGP02.phx.gbl...

> Ben Voigt [C++ MVP] <r...@nospam.nospam> wrote:
>> "Igor Tandetnik" <itand...@mvps.org> wrote in message
>> news:Oy5MjBFZ...@TK2MSFTNGP03.phx.gbl...
>>>> No, I meant what I wrote
>>>>
>>>> void (Y::*pf)() = &X::foo;
>>>
>>> If it were able to do so, it would be able to call foo on some other
>>> class Z derived from X. But it shouldn't be able to call a protected
>>> method on an unrelated class, even if the two share the same base.
>>
>> How? I declared pf in such a way that it can only be used on Y
>> instances, not X or any class derived from X but not Y, such as your
>> Z. What is the problem?
>
> There's no access control check on assignment. As soon as &X::foo is a
> valid expression, you can assign it to a variable of type void (X::*)()
> without any further checks. So the compiler performs access control on
> address-of, and disallows &X::foo inside Y.

There's a type check on assignment, though. &X::foo inside Y would need to
be an expression of type void (Y::*)(), no further access control check
would be needed.

If the C++ standard doesn't allow it, I understand. But it would be correct
(just like generic covariance would be correct, but the C# standard doesn't
permit it).

Ben Voigt [C++ MVP]

unread,
Jan 31, 2008, 6:17:02 PM1/31/08
to

"Igor Tandetnik" <itand...@mvps.org> wrote in message
news:ejLc8UFZ...@TK2MSFTNGP05.phx.gbl...

> Ben Voigt [C++ MVP] <r...@nospam.nospam> wrote:
>> "Alex Blekhman" <tkfx....@yahoo.com> wrote in message
>> news:ex7dUDFZ...@TK2MSFTNGP03.phx.gbl...
>>> Actually, I started this thread because the C++ compiler doesn't
>>> accept that syntax. Igor cited relevant part of the Standard that
>>> explicitly forbids it and demonstrated with short example why it
>>> forbids such sintax.
>>
>> Igor's argument applies to
>>
>> void (X::*pf)() = &X::foo; // inside Y::bar
>>
>> not what I wrote which is
>>
>> void (Y::*pf)() = &X::foo; // inside Y::bar
>
> Again, the compiler won't even get to the assignment part. Just writing a
> no-op like
>
> &X::foo;
>
> will already fail to compile.
>
>> My version, which may or may not be accepted by the compiler, is
>> perfectly typesafe.
>
> Again, nobody doubts the type safety. It's access control that's blocking
> your code.

But Y has access to the X::foo member of any Y object.

Perhaps some syntax like &Y::X::foo or &Y::__super::foo?

Igor Tandetnik

unread,
Jan 31, 2008, 6:31:40 PM1/31/08
to
Ben Voigt [C++ MVP] <r...@nospam.nospam> wrote:
> "Igor Tandetnik" <itand...@mvps.org> wrote in message
> news:%23WcJKQF...@TK2MSFTNGP02.phx.gbl...
>> Ben Voigt [C++ MVP] <r...@nospam.nospam> wrote:
>>> "Igor Tandetnik" <itand...@mvps.org> wrote in message
>>> news:Oy5MjBFZ...@TK2MSFTNGP03.phx.gbl...
>>>>> No, I meant what I wrote
>>>>>
>>>>> void (Y::*pf)() = &X::foo;
>>>>
>>>> If it were able to do so, it would be able to call foo on some
>>>> other class Z derived from X. But it shouldn't be able to call a
>>>> protected method on an unrelated class, even if the two share the
>>>> same base.
>>>
>>> How? I declared pf in such a way that it can only be used on Y
>>> instances, not X or any class derived from X but not Y, such as your
>>> Z. What is the problem?
>>
>> There's no access control check on assignment. As soon as &X::foo is
>> a valid expression, you can assign it to a variable of type void
>> (X::*)() without any further checks. So the compiler performs access
>> control on address-of, and disallows &X::foo inside Y.
>
> There's a type check on assignment, though. &X::foo inside Y would
> need to be an expression of type void (Y::*)(), no further access
> control check would be needed.

What do you mean, "would need to be"? Are you proposing a change to the
language? So you are saying, &X::foo should be of type void (Y::*)()
since foo is protected, but &X::xyz (where xyz is a public method of X)
should be void (X::*)(), right? I don't think there's any precedent in
C++ language for an expression to change its type depending on access to
names that participate in it. C++ tries to keep type system and access
control decoupled.

Igor Tandetnik

unread,
Jan 31, 2008, 6:33:17 PM1/31/08
to
Ben Voigt [C++ MVP] <r...@nospam.nospam> wrote:
> But Y has access to the X::foo member of any Y object.
>
> Perhaps some syntax like &Y::X::foo or &Y::__super::foo?

Why not simply &Y::foo ?

Doug Harrison [MVP]

unread,
Jan 31, 2008, 6:33:00 PM1/31/08
to
On Thu, 31 Jan 2008 16:51:44 -0500, "Igor Tandetnik" <itand...@mvps.org>
wrote:

>Doug Harrison [MVP] <d...@mvps.org> wrote:


>> I was thinking the same thing, but it doesn't seem like the compiler
>> would let you say the following even if the hypothetical
>> initialization worked:
>>
>> (pz->*pf)();
>>
>> for the same reason it doesn't allow:
>>
>> pz->foo();
>
>Why? The compiler doesn't know that pf is bound to foo. It could be
>bound to some public method xyz() of X, and pz->xyz() is perfectly fine.

Thanks, I was wrongly focusing on the initialization, where the value of pf
was evident:

>>> void (X::*pf)() = &X::foo; // hypothetical, doesn't compile
>>> (pz->*pf)(); // same as pz->foo()

Of course, pf doesn't carry any accessibility information with it.

Alex Blekhman

unread,
Feb 1, 2008, 11:02:05 AM2/1/08
to
"Igor Tandetnik" wrote:
>> Obviously the "void (Y::*pf)() = &Y::foo;" statement will work.
>> However, I meant
>>
>> void (X::*pf)() = &Y::foo;
>>
>> statement. If `X' actualy contains `foo' member, then
>> everything
>> compiles happily. If `foo' exists only in `Y', then you'll get
>> compilation error:
>>
>> C2440: cannot convert from 'void (__thiscall Y::* )(void)' to
>> 'void (__thiscall X::* )(void)'
>
> This statement should fail regardless of whether X::foo exists
> or not. It's a type mismatch - the actual values are irrelevant.
> If it actually compiles under some circumstances, I believe it's
> a compiler bug.

I beg to differ. According to the following section of the
Standard the actual type of "&Y::foo" expression is "a pointer to
foo member of X":

<quote>
5.3.1/2 Unary operators

[...] If the member is a nonstatic member of class C of type T,
the type of the result is "pointer to member of class C of typeT."
[Example:

struct A { int i; };
struct B : A { };
... &B::i ... // has type int A::*

-end example]
</quote>

The same rule is mentioned in 11.5/1 example. See the body of `fr'
function:

...
int B::* pmi_B2 = &D2::i; // OK (type of &D2::i is int B::*)
...

Alex


Igor Tandetnik

unread,
Feb 1, 2008, 11:33:35 AM2/1/08
to
Alex Blekhman <tkfx....@yahoo.com> wrote:

> "Igor Tandetnik" wrote:
>> This statement should fail regardless of whether X::foo exists
>> or not. It's a type mismatch - the actual values are irrelevant.
>> If it actually compiles under some circumstances, I believe it's
>> a compiler bug.
>
> I beg to differ. According to the following section of the
> Standard the actual type of "&Y::foo" expression is "a pointer to
> foo member of X":

Interesting. So &X::foo and &Y::foo have the exact same value and the
same type (pointer-to-member-of-X), but access check fails for the
former and passes for the latter on purely syntactical grounds. But that
does seem to mean I can call a protected method on an unrelated class:

class X {
protected:
void foo();
};

class Z : public X {};

class Y : public X {
public:
void bar() {

Z z;
// z.foo(); // doesn't compile


void (X::*pf)() = &Y::foo;

(z.*pf)(); // compiles; calls z.foo()
}
};

Looks to me like a hole in the standard.

Alex Blekhman

unread,
Feb 1, 2008, 12:35:28 PM2/1/08
to
"Igor Tandetnik" wrote:
> Interesting. So &X::foo and &Y::foo have the exact same value
> and the same type (pointer-to-member-of-X), but access check
> fails for the former and passes for the latter on purely
> syntactical grounds.

I think, there is more to it than just syntax. If `Y' actually
contains `foo' member, then "&Y::foo" yields
"pointer-to-member-of-Y" and compilation rightfully fails. So, it
seems that compiler searches starting from `Y' namespace upward
for the `foo' name. The scope where `foo' is found determines
resulting type.

> But that does seem to mean I can call a protected method on an
> unrelated class:
>
> class X {
> protected:
> void foo();
> };
>
> class Z : public X {};
>
> class Y : public X {
> public:
> void bar() {
> Z z;
> // z.foo(); // doesn't compile
> void (X::*pf)() = &Y::foo;
> (z.*pf)(); // compiles; calls z.foo()
> }
> };
>
> Looks to me like a hole in the standard.

Moreover, suppose "void (X::*pf)() = &Y::foo;" statement won't
compile. Then you could legally circumvent it with `static_cast'
(5.2.9/9):

Z z;
// z.foo(); // doesn't compile

void (Y::*pf)() = &Y::foo;

void (X::*pf2)() = static_cast<void (X::*)()>(pf);
(z.*pf2)(); // compiles; calls z.foo()


Alex


Ben Voigt [C++ MVP]

unread,
Feb 4, 2008, 9:05:06 AM2/4/08
to

"Igor Tandetnik" <itand...@mvps.org> wrote in message
news:u7OIyGGZ...@TK2MSFTNGP05.phx.gbl...

> Ben Voigt [C++ MVP] <r...@nospam.nospam> wrote:
>> But Y has access to the X::foo member of any Y object.
>>
>> Perhaps some syntax like &Y::X::foo or &Y::__super::foo?
>
> Why not simply &Y::foo ?

Because that is the overridden method, or possibly a
reference-to-vtable-member.

0 new messages