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

时间:2011-09-15 10:37:49

标签: c++ compiler-errors function-pointers typedef member-function-pointers

在得到this question的答案后,我发现有两种有效的方法可以输入定义函数指针。

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

void foo () {}

Function * p = foo;
PFunction  q = foo;

我现在更喜欢Function * pPFunction 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 ();
}

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

处收到编译错误
  

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

Demo for error

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

3 个答案:

答案 0 :(得分:10)

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

背景

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

忘记成员函数一段时间,只要想一想 member-data ,看看这段代码:

struct X
{
   int a;
};

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

它定义了一个指向成员数据的指针,而cout使用它来打印对象a的{​​{1}}的值,它打印:

x

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

现在想想,如果100 (而不是X::pa)被允许这样做,那么你已经将上述内容写成:

X::*pa

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

事实上,您无法撰写static,您需要撰写X::a。语法&X::a会导致编译错误(请参阅this)。


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

X::a

那么您认为以下代码是做什么的?

typedef void fun();

好吧,它定义类型struct X { fun a; }; 的成员a(函数不带参数,返回void),并且相当于:

fun

惊讶?请继续阅读。

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

相似性是右侧的synatax:X x; x.a(); //normal function call void (X::*pa)() = &X::a; //pointer-to-member (x.*pa)(); //using pointer-to-member &X::a是指成员函数还是成员数据,语法是相同的。

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

<强>结论:

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

答案 1 :(得分:2)

准确地说,非成员指针的两个typedef是不一样的:

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

答案 2 :(得分:0)

让我们跳过“功能”部分一秒钟。在C ++中,我们有intint*int Foo::*类型。这是一个常规整数,指向整数的指针,以及指向整数成员的指针。没有第四种类型的“整数成员”。

完全相同的函数:即使有函数类型,函数指针类型和成员函数指针类型,也没有类型“成员函数”。