不允许成员函数的非指针类型定义

Non-pointer typedef of member functions not allowed?

本文关键字:指针 类型 定义 成员 函数 不允许      更新时间:2023-10-16

得到这个问题的答案后,我发现有两种有效的方法来定义函数指针的类型。

typedef void (Function) ();
typedef void (*PFunction) ();
void foo () {}
Function * p = foo;
PFunction  q = foo;

我现在更喜欢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 << "D1n";
    }
};
struct D2 : public Base {
    void x () {
        std :: cout << "D2n";
    }
};  
int main () {
    D1 d1;
    D2 d2;
    d1 .go ();
    d2 .go ();
}

但是如果我将其更改为新的首选样式:typedef void (Base :: Callback) ()Callback * cb,我在typedef

处得到编译器错误

在成员'Callback'上附加限定'Base::'

演示错误。

为什么不允许?这只是一个疏忽还是会造成问题?

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

的背景

::是作用域解析操作符,如果X是类类型,则X::Y语法保留用于static成员访问。因此,X::*Z是另一种用于定义指针到成员的语法。暂时忘记member-function,只考虑member-data,看看下面的代码:

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

它定义了一个指向成员数据的指针,cout用它来输出对象xa的值,它输出:

100

Demo: http://www.ideone.com/De2H1

现在想想,如果X::pa(而不是X::*pa)被允许这样做,那么你就把上面的代码写成:

int X::pa = X::a; //not &X::a

看到这个语法,你如何判断X::astatic成员还是非静态成员?这就是为什么标准提出了指针到成员语法,并将其统一应用于非静态成员数据非静态成员函数的原因之一。

事实上,不能X::a,你必须写&X::a。语法X::a会导致编译错误(参见此)。


现在将member-data的参数扩展为member-function。假设您有一个定义为:

的类型定义:
typedef void fun();

那么你认为下面的代码是做什么的?

struct X
{
   fun a;
};

它定义了fun类型的成员a(这是一个不带参数的函数,返回void),它等价于这个:

struct X
{
   void a();
};

惊讶吗?读下去。

struct X
{
   fun a; //equivalent to this: void a();
};
void X::a() //yes, you can do this!
{
     cout << "haha" << endl;
}

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

X x;
x.a(); //normal function call
void (X::*pa)() = &X::a; //pointer-to-member
(x.*pa)(); //using pointer-to-member

相似的是右边的语法:&X::a。无论a引用成员函数还是成员数据,语法都是一样的。

Demo: http://www.ideone.com/Y80Mf

结论:

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

确切地说,在非成员指针的情况下,这两个类型定义是不相同的:

typedef void function();
typedef void (*fptr)();

第一个将function定义为不接受参数并返回void函数,而第二个将ftpr定义为指向不接受参数并返回void 的函数的指针。在许多上下文中,函数类型将隐式地转换为指针类型,这可能会引起混淆。但不是全部:

function f;            // declares void f();
struct test {
   function f;         // declares void test::f()
};
void g( function f );  // declares g( void (*f)() ): function decays to pointer to function in declaration
g( f );                // calls g( &f ): function decays to pointer to function
void f() {}            // definition of f
// function h = f;     // error: cannot assign functions
function *h = f;       // f decays to &f

让我们暂时跳过"function"部分。在c++中,我们有intint*int Foo::*类型。这是一个普通整数,一个指向整型的指针,一个指向整型成员的指针。没有第四个类型的"整型成员"

同样适用于函数:没有"成员函数"类型,尽管有函数类型、函数指针类型和成员函数指针类型。

相关文章: