20

C++ FAQ Lite の次の内容が当てはまる場合: 「関数名は関数へのポインターに減衰します」(配列名がその最初の要素へのポインターに減衰するため); アンパサンドを含める必要があるのはなぜですか。

typedef  int (Fred::*FredMemFn)(char x, float y);
FredMemFn p = &Fred::f;

だけでなく:

typedef  int (Fred::*FredMemFn)(char x, float y);
FredMemFn p = Fred::f;

2 番目のケースでは、Fred::f は関数であり、その関数へのポインターに減衰する可能性があります。

この質問がそれほどばかげていないことを願っています。

4

1 に答える 1

22

元の答え:

メンバー関数は関数ではなく、メンバー関数ポインターは関数ポインターではないためです。したがって、崩壊の法則は適用されません。

また、C++ には関数型がありますが、メンバ関数型はありません。したがって、関数へのポインターが期待される場所で関数を使用できますが、そのようなものはなく、メンバー関数へのポインターのみであるため、メンバー関数は使用できません。あなたの例の f は関数です。一方、Fred::f は...まあ、何もありません。

また、「関数の名前は崩壊する可能性があります...」と主張します。いいえ、名前は何もできません。関数型の左辺値は暗黙的に関数へのポインターに変換できます。オーバーロードの解決に関する限り、これは ID 変換です。

私の答えを明確にするための編集:

C++ のすべての式には型と値があります。ある型の値を別の型の値に変換できる場合があります。これらの変換は、主に関数オーバーロードの解決のために、ある変換を別の変換よりも優れたものにするためにランク付けされます。

変換のタイプの 1 つは、左辺値から右辺値への変換と呼ばれます。右辺値が必要なコンテキストに左辺値が現れると、この変換が行われます。通常、この種の変換は何も行いません。次に例を示します。

int i = 4, j = 5;
i = j;

2 行目の j は左辺値ですが、ここでは右辺値が必要なので、j は右辺値に変換されます。しかし、これは観測可能な変換ではありませんね。ただし、左辺値から右辺値への変換が観察される場合があります。つまり、n T の配列の左辺値は、値が配列T*の最初の要素のアドレスである型の右辺値と、 「シグネチャ S を持つ関数」型の左辺値、および 「署名付き関数へのポインタ」型の右辺値に変換できます。値が関数のアドレスであるシグネチャ S"

つまり、関数を関数へのポインタに割り当てると、関数の左辺値が暗黙的にそのアドレスに変換されます。

void f() {}
void (*p) () = f; //f is converted to rvalue

f は式で、型があります。f の型はvoid()

C++ には、member-function メンバー関数へのポインターはありますが、メンバー関数自体はありません。もちろん、非静的関数について話しています。静的関数は通常の関数と同じように機能します。つまり、 を記述する必要は&X::fなく、代わりにX::f Why? X::f には型関数があり、上記の変換が行われるためです。ただし、f が静的でない場合、X::f の型は...何ですか? そうそう、型がないため式ではないため、値がないため、その値は何にも変換できません。

標準からの引用: 5.3.1 節 3 メンバーへのポインターは、明示的な & が使用され、そのオペランドが括弧で囲まれていない修飾 ID である場合にのみ形成されます。[注: つまり、修飾 ID が括弧で囲まれている式 &(修飾 ID) は、「メンバーへのポインター」型の式を形成しません。関数型の左辺値から「関数へのポインター」型への暗黙的な変換があるように、非静的メンバー関数の修飾 ID から「メンバー関数へのポインター」型への暗黙的な変換がないため、修飾 ID も同様です (4.3 )。&unqualified-id は、unqualified-id のクラスのスコープ内であっても、メンバーへのポインターではありません。]

これがより明確であることを願っています...

于 2010-10-24T14:40:13.460 に答える