【问题标题】:Non-pointer typedef of member functions not allowed?不允许成员函数的非指针类型定义?
【发布时间】:2011-11-17 18:35:09
【问题描述】:

在得到this question 的答复后,我发现有两种有效的方法来定义函数指针。

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 << "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.

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

【问题讨论】:

  • @Als 现在把 * 从 typedef 中去掉,你就会明白他的意思了。
  • @Als,不是doesn't compile
  • 准确地说:typedef void Function(); 定义了类型不带参数且不返回任何内容的函数的别名,注意其中没有指针那里的类​​型...该 typedef 定义了函数的签名,而不是指向具有该签名的函数的指针。

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


【解决方案1】:

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

背景

:: 是范围解析运算符,如果 X 是类类型,则语法 X::Y 保留给 static 成员访问。所以X::*Z 是为定义pointer-to-member而发明的另一种语法。

暂时忘记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

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

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

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

看到这个语法,你怎么知道X::astatic 成员还是非静态成员?这就是标准提出 pointer-to-member 语法并将其统一应用于 non-static member-data 以及 non-静态成员函数.

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


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

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

相似之处是右侧的语法:&amp;X::a。不管a是指成员函数还是成员数据,语法都是一样的。

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

结论:

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

【讨论】:

  • static 成员访问的论点非常合理。但是,还有一点需要解决,对于成员函数指针语法,我们将其括在 () 中,这将与 static 成员区分开来。
  • @iammilind:我不太听从你的评论......在成员函数的情况下,在非成员函数的情况下,你必须用() 括起来有趣地解释为在声明中包含 除了括号内的所有内容(C 声明语法的优点)之外的所有内容,以区分指向函数的指针和返回指针的函数。我看不出static 成员与那组括号有什么关系,或者我误解了你的评论?
  • @David,根据 Nawaz 的回答,:: 保留给static 成员访问(在 LHS 与 class 一起使用时)。所以编译器很难解释:: 是用于类成员函数指针声明的。我同意这一点。 .....但是,可以说成员函数指针仍然可以通过我们添加的额外()来区分。例如。 typedef void (Base :: Callback) (); ... 在这种情况下,我们仍然可以说 Callback 应该用于班级成员,而不是 static 成员;因为它包含在() 中。
  • 我很确定你在int X::pa=X::a; 上的最后一节是不相关的,因为它本身既没有意义,也没有成员函数的类比。你说的是objects而不是types
  • @spraff:因为在非成员函数的情况下,Function 不仅仅存在于语法Function *x。它还有其他用途,而 PFunction 不能 被使用。一个例子是我的回答本身struct X { fun a; };。在这里尝试使用 typedef void (*fun)()。它行不通。但在成员函数的情况下,没有其他用途。因此,如果唯一的行是:MemFunction *x,那么为什么不让MemFunction 本身就是指针,因为* 是语法的基本部分。
【解决方案2】:

确切地说,在非成员指针的情况下,这两个 typedef 是不一样的:

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

第一个将function定义为一个不带参数并返回void的函数,而第二个将ftpr定义为一个指向不带参数并返回@987654326的函数的指针@。由于函数类型将在许多上下文中隐式转换为指针类型,因此可能会出现混淆。但不是全部:

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

【讨论】:

  • 你的remove_ptr 不起作用:ideone.com/mkifj 我猜这意味着指向void() 类型的指针与void(*)() 类型不同?!
  • @spraff:你说得对,remove_ptr 似乎无法从指向成员的指针 中删除指针。现在我想了想,这在技术上是不可能的,因为 指向成员的指针 不是作为真正的指针实现的,而是作为包含不同信息的复杂结构实现的:对于成员变量,它将包含距对象开头的偏移量,对于非虚函数,它将是指向实际函数的指针,对于虚函数,它必须包含与 vtable 的偏移量...我将删除该部分答案。
【解决方案3】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-12
    • 2015-09-04
    • 1970-01-01
    相关资源
    最近更新 更多