不允许成员函数的非指针typedef?

spr*_*aff 17 c++ typedef compiler-errors member-function-pointers function-pointers

在得到这个问题的答案后,我发现有两种有效的方法可以输入一个函数指针.

typedef void (Function) ();
typedef void (*PFunction) ();

void foo () {}

Function * p = foo;
PFunction  q = foo;
Run Code Online (Sandbox Code Playgroud)

我现在更喜欢Function * p,PFunction q但显然这对指针到成员函数不起作用.考虑这个人为的例子.

#include <iostream>

struct Base {
    typedef void (Base :: *Callback) ();
                        //^^^ remove this '*' and put it below (i.e. *cb)
    Callback cb;

    void go () {
        (this->*cb) ();
    }

    virtual void x () = 0;

    Base () {
        cb = &Base::x;
    }
};

struct D1 : public Base {
    void x () {
        std :: cout << "D1\n";
    }
};

struct D2 : public Base {
    void x () {
        std :: cout << "D2\n";
    }
};  

int main () {
    D1 d1;
    D2 d2;
    d1 .go ();
    d2 .go ();
}
Run Code Online (Sandbox Code Playgroud)

但是,如果我将其更改为新的首选样式:typedef void (Base :: Callback) ()并且Callback * cb,我得到编译器错误typedef

成员'回调'的额外资格'Base ::'

演示错误.

为什么不允许这样做?这只是一种疏忽还是会导致问题?

Naw*_*waz 10

对于非成员函数,类型typedef void(Function)()有多种用途,但对于成员函数,唯一的应用是声明一个包含函数指针的变量.因此,除了样式偏好之外,没有严格的需要允许这种语法,并且它已从标准中省略.

背景

::是作用域解析运算符,如果是类类型,X::Y则保留语法用于static成员访问X.因此X::*Z发明了另一种语法来定义指向成员的指针.

忘记成员函数一段时间,只需考虑成员数据,并查看以下代码:

struct X
{
   int a;
};

int X::*pa = &X::a; //pointer-to-member
X x = {100}; //a = 100
cout << (x.*pa) << endl;
Run Code Online (Sandbox Code Playgroud)

它定义了一个指向成员数据指针,并cout使用它来打印a对象的值x,并打印:

100
Run Code Online (Sandbox Code Playgroud)

演示:http://www.ideone.com/De2H1

现在想想,如果X::pa(相对于X::*pa)被允许这样做,那么你已经将上述内容写成:

int X::pa = X::a; //not &X::a
Run Code Online (Sandbox Code Playgroud)

看到这种语法,您如何判断X::astatic成员还是非静态成员?这就是标准提出指向成员语法的一个原因,并将其统一应用于非静态成员数据以及非静态成员函数.

事实上,你不能X::a,你要写&X::a.语法X::a会导致编译错误(请参阅此内容).


现在将member-data的这个参数扩展到member-function.假设你有一个typedef定义为:

typedef void fun();
Run Code Online (Sandbox Code Playgroud)

那你认为以下代码怎么办?

struct X
{
   fun a;
};
Run Code Online (Sandbox Code Playgroud)

好吧,它定义a了类型的成员fun(函数不带参数,返回void),相当于:

struct X
{
   void a();
};
Run Code Online (Sandbox Code Playgroud)

惊讶吗?继续阅读.

struct X
{
   fun a; //equivalent to this: void a();
};

void X::a() //yes, you can do this!
{
     cout << "haha" << endl;
}
Run Code Online (Sandbox Code Playgroud)

我们可以使用完全相同的语法来引用a哪个现在是成员函数:

X x;
x.a(); //normal function call

void (X::*pa)() = &X::a; //pointer-to-member
(x.*pa)(); //using pointer-to-member
Run Code Online (Sandbox Code Playgroud)

相似性是右侧的synatax : &X::a. 无论a是指成员函数还是成员数据,语法都是相同的.

演示:http://www.ideone.com/Y80Mf

结论:

我们知道X::a无论a是成员数据还是成员函数,我们都无法写入RHS .允许的唯一语法是&X::f,必须使目标类型(在LHS上)也必须是指针,这反过来使语法void (X::*pa)()绝对必要和基本,因为它适合语言中的其他语法.