T::f
次の両方が objectの関数を呼び出しますt
。
1. t.f();
2. t.T::f();
3. (t.*&T::f)();
もう一方が使用されていない場所で 2 番目が使用されているのを見てきました。これら2つの違いは何ですか?また、どのような状況でどちらを優先する必要がありますか?
ありがとう。
編集:申し訳ありませんが、2番目のものを忘れていましたt.T::f()
。私はそれを追加しました。
T::f
次の両方が objectの関数を呼び出しますt
。
1. t.f();
2. t.T::f();
3. (t.*&T::f)();
もう一方が使用されていない場所で 2 番目が使用されているのを見てきました。これら2つの違いは何ですか?また、どのような状況でどちらを優先する必要がありますか?
ありがとう。
編集:申し訳ありませんが、2番目のものを忘れていましたt.T::f()
。私はそれを追加しました。
t.f()
との呼び出し(t.*&T::f)()
は意味的に同一であり、メンバー関数を呼び出す "通常の" 方法です。オーバーライドされた仮想関数であっても、呼び出しt.T::f()
は呼び出します。T::f()
f()
式(t.*&T::f)()
は、メンバー関数へのポインターを取得することによって、メンバー関数を呼び出します。この式が持つ可能性がある唯一の潜在的な影響は、一部のコンパイラで関数のインライン展開が禁止される可能性があることです。から取得した変数を使用し&T::f
てメンバー関数を呼び出すことはカスタマイズ ポイントですが、関数を直接呼び出すことは単なる難読化であり、ペシミゼーションになる可能性があります (コンパイラがアドレスが変更できないことを検出するのに十分な const 伝播を実行できない場合)。 .
私が想像できるのは、誰かがの仮想関数呼び出しを禁止しようとt
したことです。もちろん、これはこの方法では機能しません。これは、メンバーへのポインターが引き続き正しい仮想関数を呼び出すためです。仮想ディスパッチを禁止するには、 を使用しますt.T::f()
。
t.f()
よりも優先する必要があり(t.*&T::f)()
ます。仮想ディスパッチを禁止したい場合は、t.T::f()
それ以外の場合は を使用しますt.f()
。仮想ディスパッチを禁止する主な用途は、オーバーライド関数内から関数の基底クラス バージョンを呼び出すことです。それ以外の場合は、めったに役に立ちません。
最初のものは通常のものです。それはあなたが好むべきものです。2 つ目は、 へのメンバ関数ポインタを取り、f
に対して逆参照してt
から呼び出します。
その余分な旅行に実際の利益がある場合、私はそれを認識していません. フリー関数*&f()
呼び出し時のメンバ版です。
後で追加したt.T::f()
は静的にディスパッチされるため、それが仮想であり、独自の実装を持つの派生クラスであったとしてもf
、が呼び出されます。仮想呼び出しメカニズムを効果的に抑制します。T
t
T
2 つ目は無意味です。難読化されたコードです。いいえ、仮想ディスパッチもインライン化も無効にしません。いずれの場合も、どちらもまったく同じことを行います。