1. ホーム
  2. c++

[解決済み】関数ポインタの定義にアンパサンド '&' やアスタリスク '*' がいくつあっても大丈夫なのはなぜですか?

2022-04-05 05:26:51

質問

なぜ、次のような仕組みになっているのでしょうか?

void foo() {
    cout << "Foo to you too!\n";
};

int main() {
    void (*p1_foo)() = foo;
    void (*p2_foo)() = *foo;
    void (*p3_foo)() = &foo;
    void (*p4_foo)() = *&foo;
    void (*p5_foo)() = &*foo;
    void (*p6_foo)() = **foo;
    void (*p7_foo)() = **********************foo;

    (*p1_foo)();
    (*p2_foo)();
    (*p3_foo)();
    (*p4_foo)();
    (*p5_foo)();
    (*p6_foo)();
    (*p7_foo)();
}

解決方法は?

これには、これらの演算子の組み合わせがすべて同じように動作するようにするための、いくつかの断片があります。

これらがすべて機能する根本的な理由は、関数(例えば foo ) は、暗黙のうちに関数へのポインタに変換されます。 このため void (*p1_foo)() = foo; が動作します。 foo は暗黙のうちに自分自身へのポインタに変換され、そのポインタは p1_foo .

単項の & を関数に適用すると、オブジェクトに適用するとオブジェクトのアドレスが得られるのと同じように、関数へのポインタが得られます。 通常の関数へのポインタの場合は、暗黙のうちに関数から関数へのポインタに変換されるため、常に冗長です。 いずれにせよ、このような理由で void (*p3_foo)() = &foo; が機能します。

単項の * 関数ポインタに適用すると、オブジェクトへの通常のポインタに適用したときと同じように、指されたオブジェクトを生成します。

これらのルールは組み合わせることができます。 最後から2番目の例を見てみましょう。 **foo :

  • 最初に foo は暗黙のうちに自分自身へのポインタに変換され、最初の * がその関数ポインタに適用され、その結果、関数 foo を再び使用します。
  • そして、その結果を再び暗黙のうちに自分自身へのポインタに変換して、2番目の * が適用され、再び関数 foo .
  • その後、再び関数ポインタに暗黙のうちに変換され、変数に代入されます。

をいくつでも追加することができます。 * を好きなだけ使っても、結果はいつも同じです。 より多くの * があればあるほど、楽しい。

5つ目の例も考えてみましょう。 &*foo :

  • 最初に foo は暗黙のうちに自分自身へのポインタに変換されます。 * が適用され foo また
  • 次に & に適用されます。 foo へのポインタを生成します。 foo となり、変数に代入される。

は、その & は関数にしか適用できませんが、関数ポインタに変換された関数には適用できません (もちろん、関数ポインタが変数の場合は例外で、その場合は結果は関数へのポインタになります; 例えば、リストに void (**pp_foo)() = &p7_foo; ).

このため &&foo は機能しません。 &foo は関数ではなく、rvalueである関数ポインタです。 しかし &*&*&*&*&*&*foo と同じように動作します。 &******&foo というのは、これらの表現ではどちらも & は常に関数に適用され、rvalue関数ポインタには適用されません。

また、単項の * は、関数ポインタを介して呼び出すことができます。 (*p1_foo)();(p1_foo)(); が同じ結果になるのは、やはり関数から関数ポインタへの変換が原因です。