17

この質問に対する回答を得た後、関数ポインターを型定義する有効な方法が 2 つあることを発見しました。

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) ()andに変更するとCallback * cb、次の時点でコンパイラ エラーが発生します。typedef

メンバー 'Callback' の追加修飾 'Base::'

エラーのデモ

これが許可されないのはなぜですか?それは単なる見落としですか、それとも問題を引き起こすのでしょうか?

4

3 に答える 3

10

非メンバー関数の場合、 のような型にtypedef void(Function)()はいくつかの用途がありますが、メンバー関数の場合、唯一のアプリケーションは、関数ポインターを保持する変数を宣言することです。したがって、文体上の好みを除けば、この構文を厳密に許可する必要はなく、標準から省略されています。

バックグラウンド

::スコープ解決演算子であり、がクラス型の場合、構文はメンバー アクセスX::Y用に予約されています。同じように、 pointer -to-memberを定義するために発明された別の構文もありました。staticXX::*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;

これは、pointer-to-member-data を定義し、それを使用してobjectcoutの値を出力し、以下を出力します。ax

100

デモ: http://www.ideone.com/De2H1

X::pa( とは対照的にX::*pa) がそれを行うことが許可されている場合、上記を次のように記述したと考えてください。

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

X::aこの構文を見て、staticメンバーか非静的メンバーかをどのように判断しますか? これが、標準がポインターからメンバーへの構文を思いついた理由の 1 つであり、それを非静的 member-data非静的 member-functionに一様に適用します。

実際には、書くことはできません。書く必要がありX::aます&X::a。この構文X::aでは、コンパイル エラーが発生します (これを参照してください)。


member-dataのこの引数をmember-functionに拡張します。次のように定義された typedef があるとします。

typedef void fun();

次に、次のコードは何をしていると思いますか?

struct X
{
   fun a;
};

さて、それはatype のメンバーfun(引数を取らず、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::aaメンバー関数またはメンバー データのどちらを参照する場合でも、構文は同じです。

デモ: http://www.ideone.com/Y80Mf

結論:

メンバーデータかメンバー関数かにX::a関係なく、RHS に書き込むことはできないことがわかっています。a許可されている唯一の構文は、(LHS の) ターゲット型もポインター&X::fなければならないことを必要とするものであり、その構文は言語の他の構文に適合するため、絶対に必要かつ基本的なものになります。void (X::*pa)()

于 2011-09-15T11:00:31.203 に答える
2

正確には、非メンバー ポインターの場合の 2 つの typedef は同じではありません。

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

1 つ目は、引数を取らずに を返すfunction関数として定義し、2 つ目は、引数を取らずに を返す関数へのポインタとして定義します。多くのコンテキストで関数型が暗黙的にポインター型に変換されるため、おそらく混乱が生じます。すべてではありません:voidftprvoid

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
于 2011-09-15T11:08:10.157 に答える
0

「機能」の部分はちょっと飛ばしてみましょう。C++ には、int、 、int*およびint Foo::*型があります。これは、通常の整数、整数へのポインター、および整数メンバーへのポインターです。4 番目のタイプの「整数メンバー」はありません。

まったく同じことが関数にも当てはまります。関数型、関数ポインター型、およびメンバー関数ポインター型があっても、「メンバー関数」という型はありません。

于 2011-09-15T12:12:28.957 に答える