2017-11-09 1 views
2

ルック:ここメンバー関数ポインタは、関数が実際に宣言したクラスに基づいた型を持つのはなぜですか?このスニペットで

struct A { 
    void fn(); 
}; 

struct B: A { 
}; 

void f() { 
    auto x = &B::fn; 
} 

xは私が&B::fnを書いたという事実にもかかわらず、void (A::*)()の種類を取得します。

fnBに追加した場合、xのタイプはvoid (B::*)()となります。

したがって、タイプ&B::fnは、Bがfnであるかどうかを変更します。

この現象の背景には何がありますか?私はそれが驚くべきことだと思う。

なぜこの点が重要ですか?たとえば、プログラマXが私の例のようにABを作成するとします。プログラマYは&B::fnを使い、その型のクラス部分を何か(テンプレートのパラメータなど)に使用します。プログラマーXは、fnにいくつかの追加機能が必要であると認識して、それを上書きします。さて、タイプ&B::fnが変更されたので、プログラマのYのコードが壊れる可能性があります。

+0

'fn'がオーバーライドされると、クラスインタフェースが変更されます。プログラマーYは再コンパイルする必要があり、タイプは再び正しくなります。 – patatahooligan

+0

これは一貫した動作のようです。プログラマーYのコードは 'fn'が削除されたり名前が変更されたりすると壊れるでしょう。 – VTT

+0

@VTT:はい、通常は関数のオーバーライドを追加しても何も破られません。関数を削除/名前変更すると、コンパイル時にエラーが発生することが予想されます。オーバーライドを追加するときは無効です。 – geza

答えて

2

これはCWG issue 203EWG issue 89の件です。最初は、根拠が有効であるために、できるだけ多くのコードを許可した:04/00会議から

注:現在の治療のための

根拠がために可能な限り広範な使用を可能にすることです指定されたメンバーアドレス式で構成されます。ポインタへのポインタへのポインタは暗黙的にポインタへのポインタへの変換が可能であるため、式の型をポインタへのポインタにすることで、結果を初期化するか、 to-baseメンバまたはポインタから派生メンバへのポインタです。この提案を受け入れることは、後者の使用のみを可能にする。 4月、2015

追加注::

EWGがあると判断していることに起因する問題がより明白になった後

その後、修正するには遅すぎましたそのような変更の有用性は、コードを破るという事実よりも重要です。 012Wを参照してください。

+0

これは面白いです: "EWGは、既存のセマンティクスへの変更は実質的にタイムマシンを必要とし、これらのセマンティクスは偶然には出現しないと考えていたと考えています:) – geza

+0

hvd、btw、どのようにしてこの情報を見つけましたか?ちょうどGoogleと、またはあなたはこれを検索するためにいくつかの背景情報を持っていますか?もし私が知っていれば、私の質問のいくつかは、これらの種類の情報をどのように検索するのか不必要になるでしょう。 – geza

+0

そして、おめでとうございます... 100人の代理人、あなたは100万人になるでしょう! – geza

1

主なアイデアは、通常の "B is A"クラスの継承定義から来ていると思います。 「Aの関数はBの関数です」のようなメンバ関数については言い換えることができますが、AとBの位置を反転させた文は、「Bの一部の関数だけがAの関数です」という項目でのみ正しいです。したがって、B::fnは、Aの関数であるBのこのカテゴリの関数に適合します。fnをクラスBに書くと同時に、このカテゴリのうちB::fnをAの関数ではないBの関数のカテゴリに移動します。

これは、1つのクラスは、ベースクラスのいくつかのメソッドをオーバーライドするかどうかを確認することができる:

const bool fn_is_overriden{::std::is_same<decltype(&A::fn), decltype(&B::fn)>::value}; 
+3

これがその理由ですか、それの結果ですか?後者は私にとってもっとそうであるようです。 – hvd

0

ほとんどの方法では、メンバ関数は、暗黙オブジェクト引数とフリー関数のようです。機能のすべてにランク付けすることができるように、

例えば
struct A {}; 
void A_fn(A* this_); 

、メンバ関数とフリー機能との間のオーバーロード解決をこのように定義されますので、このコード:

struct A { 
    void fn(); 
}; 

は、と非常に類似しています同じ発足。

メンバ(変数または関数)を継承すると、その関数をそのまま継承します。継承は新しい関数や変数を定義しません。それが行うのは、派生クラス内で、基本クラスの既存のメンバーを参照する名前を作成することだけです。

あなたはまだA_fn(A* this)ですが、それはBの範囲からアクセスできるということだけです。継承のアクセス規則は継承された名前にのみ適用され、メンバのプロパティは変更されません。

関連する問題