为啥即使从类内部获取成员函数指针值也需要类名限定?

Posted

技术标签:

【中文标题】为啥即使从类内部获取成员函数指针值也需要类名限定?【英文标题】:Why does taking a member function pointer value requires class name qualification even from inside of the class?为什么即使从类内部获取成员函数指针值也需要类名限定? 【发布时间】:2015-12-26 20:13:16 【问题描述】:

当返回一个指向该类成员函数之一中的类的成员函数指针时,我仍然必须指定该类。我不能简单地接受地址。例如this code works fine:

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

但如果在getPointer 我尝试简单地做:return &amp;func 我得到这个错误:

prog.cpp:在成员函数'void (Foo::* Foo::getPointer())(int)'中: prog.cpp:8:43: 错误:ISO C++ 禁止使用不合格或带括号的非静态成员函数的地址来形成指向成员函数的指针。说“&amp;Foo::func”[-fpermissive]void (Foo::*getPointer())(int) return &amp;func;

为什么我必须在我所处的上下文中指定类?

【问题讨论】:

啊我误解了你的问题,你问Foo::&amp;Foo::func中的必要性。 @JoachimPileborg 是的,为什么编译器无法解决这个问题让我很生气。我不必指定静态函数、成员变量、调用函数时,甚至获取成员变量的地址;但由于某种原因,如果我想要一个函数的地址,我必须指定。 因为标准是这样说的。如果您认为这不合逻辑,那么您如何看待将double 分配给std::string 变量的能力? 您是否真的尝试将double 分配给std::string 变量?这是合法的......(欢迎来到地狱;-)) 请注意,对于数据成员&amp;my_class::data_m&amp;data_m 之间存在差异。对成员函数要求 &amp;my_class::mem_fun 似乎更一致。 【参考方案1】:

指针和指向成员的指针是不同的类型,我们可以从 C++ 标准草案3.9.2 [basic.compound] 部分看到,其中包括指针的复合类型以及指向非静态类的指针成员和备注:

静态类成员是对象或函数,以及指向它们的指针 是指向对象或函数的普通指针

我认为在this quote in an answer from Johannes 中从Annotated C++ Reference Manual(ARM) 中很好地描述了这个问题:

请注意,必须显式使用地址运算符来获取 指向成员的指针;没有隐式转换......如果有, 我们在成员函数的上下文中会有歧义......对于 例如,

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::*'

尤其是以下几行:

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

和:

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

演示限定名称和非限定名称之间的区别。

【讨论】:

我想我需要拿一份 ARM 的副本去我的 D&E 旁边,你不能买任何一个的 pdf :-( 我想我结合dyp's comment看你的答案。当我做&amp;B:: 时,我得到的是偏移量而不是绝对地址。我想我明白所有方法都是通过偏移量调用的,不是实际地址? @JonathanMee 是一种可能的实现方式,阅读 Pointers to member functions are very strange animals 应该会有所帮助。【参考方案2】:

(...有一个错误的答案...)

在以下情况下看起来更奇怪:

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;

输出是

你好世界 4

调用Foo::func 是在Foo 类中调用func,而调用&amp;Foo::func 是虚拟调用。

【讨论】:

非常感谢。现在一切都说得通了。 这是错误的。非静态成员函数地址不是指针,需要使用this,因此它不必在编译时解析为“地址”。此外,如果 mp 指向 Foo::func 并且您在 Bar 实例上调用它,则将调用 Bar::func 版本。 @6502 Arg!你正在否定我听到的唯一合理的解释! 它变得不合逻辑

以上是关于为啥即使从类内部获取成员函数指针值也需要类名限定?的主要内容,如果未能解决你的问题,请参考以下文章

C ++从类外部设置指向结构中函数成员的指针

在模板派生类中,为啥我需要在成员函数中使用“this->”来限定基类成员名称?

C++从类的向量指针访问类的成员

通过反射获取类的类名,方法和内部成员变量

重构 MFC 消息映射以包含完全限定的成员函数指针

Javascript - 从数组中查找所有类,从类中获取并返回内部文本