Access to method pointer to protected method?
Asked Answered
B

7

15

This code:

class B {
 protected:
  void Foo(){}
}

class D : public B {
 public:
  void Baz() {
    Foo();
  }
  void Bar() {
    printf("%x\n", &B::Foo);
  }
}

gives this error:

t.cpp: In member function 'void D::Bar()':
Line 3: error: 'void B::Foo()' is protected
  • Why can I call a protected method but not take its address?
  • Is there a way to mark something fully accessible from derived classes rather than only accessible from derived classes and in relation to said derived class?

BTW: This looks related but what I'm looking for a reference to where this is called out in the spec or the like (and hopefully that will lead to how to get things to work the way I was expecting).

Bishop answered 28/4, 2011 at 17:10 Comment(0)
Q
23

You can take the address through D by writing &D::Foo, instead of &B::Foo.

See this compiles fine : http://www.ideone.com/22bM4

But this doesn't compile (your code) : http://www.ideone.com/OpxUy


Why can I call a protected method but not take its address?

You cannot take its address by writing &B::Foo because Foo is a protected member, you cannot access it from outside B, not even its address. But writing &D::Foo, you can, because Foo becomes a member of D through inheritance, and you can get its address, no matter whether its private, protected or public.

&B::Foo has same restriction as b.Foo() and pB->Foo() has, in the following code:

void Bar() {
    B b;
    b.Foo();     //error - cannot access protected member!
    B *pB = this;
    pB->Foo();   //error - cannot access protected member!
  }

See error at ideone : http://www.ideone.com/P26JT

Queenqueena answered 28/4, 2011 at 17:18 Comment(10)
Your post doesn't answer "Why can I call a protected method but not take its address?".Fuchsia
For reason to complex to go into, I don't want to (lexically) reference the Derived class at this point.Bishop
@Prasoon: Yes. It doesn't answer (that part). Did I say it does? It partly answers his question which has two parts.Queenqueena
@Nawaz : No why would someone possibly say that his answer is complete? People expect posters to post complete answers.Fuchsia
@Prasoon: Who said my answer is complete?Queenqueena
@Prasoon: People expect posters to post complete answers... I would say "Good expectation". But I answer to the extent I know.Queenqueena
@Prasoon: +1 for an incomplete answer. { GodwinsLaw::Invoke(); }Bishop
@Bishop (and @Prasoon too) : Now it isn't incomplete :DQueenqueena
@ the 1st line: for some reason I hadn't know this was possible. Cool! So, aside from all people's grumbling about completeness, your answer would have told me something useful even before your edits.Subversive
9 years later, the ideone links are deadAshleeashleigh
R
6

This is because an object of a derived class can only access protected members of a base class if it's the same object. Allowing you to take the pointer of a protected member function would make it impossible to maintain this restriction, as function pointers do not carry any of this information with them.

Rayerayfield answered 28/4, 2011 at 17:14 Comment(4)
I don't want that restriction. Is there a way to make things work the way I was expecting? (See edit)Bishop
@Bishop I'm not really sure why the same-object restriction is there in the first place. A clue to what you're trying to accomplish with this would be helpful.Rayerayfield
@Bishop Practically speaking no because what you're asking for is a change to the C++ standard. You could always fork a C++ compiler and modify it allow such a construct but then it wouldn't be C++ anymore.Grassi
@Bishop - If you were allowed to do what you want, the protected keyword would be practically useless because anyone could get access to other classes' protected parts by just deriving a dummy class from it and create a pointer-to-member. This doesn't work, by design!Eviscerate
G
3

I believe protected doesn't work the way you think it does in C++. In C++ protected only allows access to parent members of its own instance NOT arbitrary instances of the parent class. As noted in other answers, taking the address of a parent function would violate this.

If you want access to arbitrary instances of a parent, you could have the parent class friend the child, or make the parent method public. There's no way to change the meaning of protected to do what you want it to do within a C++ program.

But what are you really trying to do here? Maybe we can solve that problem for you.

Grassi answered 28/4, 2011 at 17:28 Comment(1)
Making things public allows non derived classes access (bad). Using friend requires the author of B to know in advance of all derived classes (bad) and also gives them access to things that shouldn't be accessible (bad).Bishop
F
3

Why can I call a protected method but not take its address?

This question has an error. You cannot do a call either

B *self = this;
self->Foo(); // error either!

As another answer says if you access the non-static protected member by a D, then you can. Maybe you want to read this?


As a summary, read this issue report.

Faro answered 28/4, 2011 at 17:30 Comment(0)
F
1

Your post doesn't answer "Why can I call a protected method but not take its address?"

class D : public B {
 public:
  void Baz() {
    // this line
    Foo();
    // is shorthand for:
    this->Foo();
  }
  void Bar() {
    // this line isn't, it's taking the address of B::Foo
    printf("%x\n", &B::Foo);

    // not D:Foo, which would work
    printf("%x\n", &D::Foo);

  }
}
Felicio answered 28/4, 2011 at 17:34 Comment(3)
That may be true/relevant from a language purist standpoint, but the fact of the matter at the machine level is that the functions F::Foo and D::Foo are the same thing (proof: a derived class can call a protected base class method even across separate compilation).Bishop
@Bishop - Yes, but you can only access D::Foo if you actually have a D object. You cannot use that pointer to access Foo that is a member of any other class. That's on purpose!Eviscerate
My assertion is that D::Foo is B::Foo (under the covers) so in this case I have two ways to do things that end up doing exactly the same thing and only one is legal. This seems very contrived if you look at it from the standpoint of what actually happens on the CPU. In effect, I'm bitching that what C++ chose as its reference-to-method isn't low-level enough for my taste.Bishop
E
0

Is there a way to mark something fully accessible from derived classes rather than only accessible from derived classes and in relation to said derived class?

Yes, with the passkey idiom. :)

class derived_key
{
    // Both private.
    friend class derived;

    derived_key() {}
};

class base
{
public:
    void foo(derived_key) {}
};

class derived : public base
{
public:
    void bar() { foo(derived_key()); }
};

Since only derived has access to the contructor of derived_key, only that class can call the foo method, even though it's public.
The obvious problem with that approach is the fact, that you need to friend every possible derived class, which is pretty error prone. Another possible (and imho better way in your case) is to friend the base class and expose a protected get_key method.

class base_key
{
    friend class base;

    base_key() {}
};

class base
{
public:
    void foo(base_key) {}

protected:
    base_key get_key() const { return base_key(); }
};

class derived1 : public base
{
public:
    void bar() { foo(get_key()); }
};

class derived2 : public base
{
public:
    void baz() { foo(get_key()); }
};

int main()
{
  derived1 d1;
  d1.bar(); // works
  d1.foo(base_key()); // error: base_key ctor inaccessible
  d1.foo(d1.get_key()); // error: get_key inaccessible

  derived2 d2;
  d2.baz(); // works again
}

See the full example on Ideone.

Encompass answered 28/4, 2011 at 18:43 Comment(0)
F
0

Standard reference: https://en.cppreference.com/w/cpp/language/access#Protected_member_access

When a pointer to a protected member is formed, it must use a derived class in its declaration:
struct Base {
  protected:
    int i;
};
 
struct Derived : Base {
  void f() {
//  int Base::* ptr = &Base::i;    // error: must name using Derived
    int Base::* ptr = &Derived::i; // OK
  }
};
Flowerless answered 7/4, 2022 at 7:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.