15

When returning a member function pointer to a class within one of that class's member functions I still have to specify the class. I cannot simply take the address. For example, this code works fine:

class Foo {
public:
    void func(int param) { cout << param << endl; }
    void (Foo::*getPointer())(int) { return &Foo::func; }
};

But if in getPointer I try to simply do: return &func I get this error:

prog.cpp: In member function 'void (Foo::* Foo::getPointer())(int)':
prog.cpp:8:43: error: ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function. Say '&Foo::func' [-fpermissive]
void (Foo::*getPointer())(int) { return &func; }

Why must I specify the class when that's the context that I am within?

Bartek Banachewicz
  • 38,596
  • 7
  • 91
  • 135
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • @JoachimPileborg Scope obviously. If there is a non-member variable with the same name as the class variable I don't have to specify the class. – Jonathan Mee Sep 29 '15 at 11:19
  • 1
    Ah I misunderstood your question, you asking about the need for `Foo::` in `&Foo::func`. – Some programmer dude Sep 29 '15 at 11:20
  • 1
    @JoachimPileborg Yeah, it's driving me batty why the compiler can't figure this out. I don't have to specify for static functions, member variables, when I call functions, or even taking the address of a member variable; but for some reason if I want the address of a function I must specify. – Jonathan Mee Sep 29 '15 at 11:23
  • 1
    Beacuse the standard says so. If you think it's illogical then what do you think about the ability to assign a `double` to a `std::string` variable? – 6502 Sep 29 '15 at 11:27
  • @6502 Ummm... What are you saying here? That by questioning why the standard requires this I am asking for us to just throw out strong typing? The gcc error states that: "ISO C++ forbids" it, I want to know why. What potential confusion is avoided by requiring the class name? – Jonathan Mee Sep 29 '15 at 11:39
  • 1
    Did you actually try to assign a `double` to an `std::string` variable? It's legal... (welcome to hell ;-) ) – 6502 Sep 29 '15 at 11:49
  • @JonathanMee - The usual explanation is that no one has found it useful enough to bother writing a proposal for a language change. Compare this to the much more common `for`-statement that has gotten a shorter version `for (auto x : v) {}` because some people got tired of writing out the longer form, and presented a paper to the committee. – Bo Persson Sep 29 '15 at 11:57
  • @6502 This doesn't work `double fp = 42; string str = fp;` I'm not sure what you're saying. Can you show me somecompiling code in http://www.ideone.com – Jonathan Mee Sep 29 '15 at 11:58
  • @BoPersson If that's really the best we have for an answer I suppose it wouldn't hurt to write it up. I was just hoping to learn some black magic that this would have otherwise inhibited... – Jonathan Mee Sep 29 '15 at 12:00
  • @JonathanMee http://coliru.stacked-crooked.com/a/a077e7768a68756e it converts double -> int -> char. – Andrew Sep 29 '15 at 12:00
  • @Jonathan Mee: double fp = 42; string str = fp; -> change this to: double fp = 42; string str ; str = fp; it works.... constructor forbids it in the first case i guess. – basav Sep 29 '15 at 12:01
  • @Andrew You're just using [the `char` assignment operator](http://www.cplusplus.com/reference/string/string/operator=/) here. You've implicitly done `string str = static_cast(fp);` – Jonathan Mee Sep 29 '15 at 12:09
  • @JonathanMee I know. It's just a sequence of implicit conversions. Nothing magical. But still somewhat undesirable. – Andrew Sep 29 '15 at 12:10
  • @JonathanMee: not every `=` operator in C++ is an assignment. I said you can **assign** a `double` to an `std::string` variable, not that you can initialize an `std::string` instance with a `double`. Anyway the point was just that if you assume that C++ is always logical you're not going to get very far... much of C++ rules are because of accidents, history (backward compatibility) or just plain committee effect. – 6502 Sep 29 '15 at 12:14
  • @6052 I don't know that [I assume everything is logical](http://stackoverflow.com/questions/29967202/why-cant-i-define-a-function-inside-another-function), but I do assume that there is a reason for everything. C++ is a mature language, there isn't much, if anything, that's in the language "just cause someone felt like it." – Jonathan Mee Sep 29 '15 at 12:17
  • 1
    @JonathanMee: unfortunately the "just cause someone felt like it" is, sometimes, the correct answer. Not always of course, and not even in the majority of the cases. But it happens. – 6502 Sep 29 '15 at 12:26
  • 3
    Note that for *data members*, there's a difference between `&my_class::data_m` and `&data_m`. It seems more consistent to require `&my_class::mem_fun` for member functions. – dyp Sep 29 '15 at 13:04
  • @dyp Interesting. Is taking the `&my_class::data_m` giving me an offset rather than a physical address? It seems like it is? Perhaps that's the point of specifying the namespace? – Jonathan Mee Sep 29 '15 at 13:09
  • @dyp was that you who made a comment about D&E a little while ago and then removed it? – Shafik Yaghmour Sep 29 '15 at 23:48
  • @ShafikYaghmour Yes, I misunderstood what the ARM was saying. I thought it was referring to an ambiguity when using *pointers to member functions*, but it says there's an ambiguity *within (the context of) a member function*. (Btw I'd also love to have a digital copy of D&E, my copy on dead tree is of very bad printing quality and already in bad shape.) – dyp Sep 29 '15 at 23:53
  • 1
    @JonathanMee Be careful when talking about obtuse details of language syntax. `&my_class::data_m` does not specify any namespace. `my_class` is the name of a class, not a namespace, and a class is *not* a namespace. Similarly, `&my_class::data_m` is not the offset of `data_m` within `my_class`, it is a member variable pointer; a member variable pointer can (sometimes/often) be implemented as an offset, but you *should avoid confusing the map for the territory*: it is not an offset, that is just how it can be implemented (usually). – Yakk - Adam Nevraumont Oct 04 '15 at 18:46
  • @Yakk That's actually a really valuable comment. This question seems to necessitate a "territorial" answer, rather than a "map" answer. I was thinking that a follow-up question would probably be in order to clarify that, but I haven't taken the time to completely formulate it in my mind. – Jonathan Mee Oct 04 '15 at 20:17

2 Answers2

4

Pointers and pointer to members are distinct types, we can see that from section the draft C++ standard section 3.9.2 [basic.compound] which includes a compound type for pointer as well as pointer to non-static class member and notes:

Static class members are objects or functions, and pointers to them are ordinary pointers to objects or functions

This issue with this is I think well described in this quote in an answer from Johannes from the Annotated C++ Reference Manual(ARM):

Note that the address-of operator must be explicitly used to get a pointer to member; there is no implicit conversion ... Had there been, we would have an ambiguity in the context of a member function ... For example,

void B::f() {
    int B::* p = &B::i;   // ok
    p = B::i;         // error: B::i is an int
    p = &i;           // error: '&i'means '&this->i'
                      // which is an 'int*'

    int *q = &i;      // ok
    q = B::i;         // error: 'B::i is an int
    q = &B::i;        // error: '&B::i' is an 'int B::*'
}

In particular these lines:

int B::* p = &B::i; // OK

and:

p = &i; // error: '&i'means '&this->i' which is an 'int*'

demonstrate the difference between the qualified and the unqualified name.

Community
  • 1
  • 1
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • I guess I need to pick up a copy of ARM to go next to my D&E, you can't buy a pdf of either :-( – Shafik Yaghmour Sep 29 '15 at 13:48
  • I think I got it looking at your answer in combination with [dyp's comment](http://stackoverflow.com/questions/32842429/why-does-taking-a-member-function-pointer-value-requires-class-name-qualificatio/32845696#comment53523542_32842429). When I do `&B::` I'm getting the offset not the absolute address. I guess I'm to understand that all methods are called by offset, *not* actual address? – Jonathan Mee Sep 29 '15 at 14:03
  • 1
    @JonathanMee that is one possible implementation, reading [Pointers to member functions are very strange animals](http://blogs.msdn.com/b/oldnewthing/archive/2004/02/09/70002.aspx) should be helpful. – Shafik Yaghmour Sep 29 '15 at 16:38
3

(...there was a wrong answer...)

It looks more strange in the following context:

class Foo {
public:
    virtual void func(int param) { cout << param << endl; }
    void call(int x) { Foo::func(x); }
    void (Foo::*getPointer())(int) { return &Foo::func; }
};

class Bar : public Foo {
public:
    void func(int param) override { cout << "Hello world!" << endl; }
};


int main() {

    Foo *a = new Bar();
    auto p = a->getPointer();
    (a->*p)(4);
    a->call(4);

    return 0;
}

The output is

Hello world
4

Calling Foo::func is a call of func in Foo class while calling &Foo::func is a virtual call.

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
Andrey Nasonov
  • 2,619
  • 12
  • 26