なぜ次のように動作しますか?
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
が再び生成されます。*
_が適用され、再び関数foo
が生成されます。好きなだけ_*
_ sを追加できます。結果は常に同じです。 _*
_ sが多いほど、陽気になります。
5番目の例_&*foo
_も検討できます。
foo
は暗黙的にそれ自体へのポインターに変換されます。単項_*
_が適用され、foo
が再び生成されます。&
_がfoo
に適用され、変数に割り当てられたfoo
へのポインターが生成されます。_&
_は関数にのみ適用できますが、関数ポインターに変換された関数には適用できません(もちろん、関数ポインターが変数である場合は、結果はポインターへのポインタです) -a-pointer-to-a-function;たとえば、リストにvoid (**pp_foo)() = &p7_foo;
)を追加できます。
これが、_&&foo
_が機能しない理由です。_&foo
_は関数ではありません。右辺値である関数ポインタです。ただし、これらの式の両方で_&*&*&*&*&*&*foo
_は常に右辺値関数ポインターではなく関数に適用されるため、_&******&foo
_は、_&
_と同様に機能します。
また、関数ポインタを介して呼び出しを行うために、単項_*
_を使用する必要がないことに注意してください。 _(*p1_foo)();
_と_(p1_foo)();
_は、関数から関数へのポインター変換のため、同じ結果になります。
Cは単に基礎となるマシンの抽象化であり、これはその抽象化が漏れている場所の1つであることを覚えておくと役立つと思います。
コンピューターの観点から見ると、関数は実行されると他の命令を実行する単なるメモリーアドレスです。したがって、Cの関数自体はアドレスとしてモデル化され、おそらく、関数が指すアドレスと「同じ」であるという設計につながります。