web-dev-qa-db-ja.com

基本クラスの仮想関数を介して派生型を取得します

基本クラスの仮想関数を介してオブジェクトの派生型を取得しようとしています。私はこれを書きましたが、コンパイルされません:

_struct base {
  virtual base& get_this() {
    return *this;
  }
};

struct derived : base {
  virtual derived& get_this() override {
    return *this;
  }

  void fn();
};


int main () {
  base* pd = new derived();
  derived& x = pd->get_this(); /*ERROR*/
  x.fn();
  return 0;
}
_

...次のエラーが表示されます:baseから_derived&_を初期化できません。 _get_this_は仮想であるため、pd->get_this()が_base&_ではなく_derived&_を返すのはなぜですか?前もって感謝します!

編集:

返信が遅くなり、有益な回答とお詫びをいただき、ありがとうございました。上記がコンパイルされない理由を理解するだけでなく、問題の解決策にも関心があることを元の投稿で指定する必要がありました。私の主な問題は、fnderivedクラスに固有であり、基本クラスを介して呼び出すことができないことです。キャストを使用することで問題は確実に解決しますが、正しい型を取得するためだけにif elseコンストラクトを使用してコードを記述することは嫌いです(Scott Meyersもキャストに対してアドバイスしています:))。答えは、キャストが進むべき道であることを示しているようです。これは、少なくとも、私の問題に対するより「エレガントな」解決策を怠っていないことを安心させるものです。再度、感謝します!

9
linuxfever

C++共変戻り型 サポートは、派生型を既に知っている場合にのみ機能します。 downcast 基本クラスから派生クラスの可能性がある場合は、dynamic_cast<derived>(base_ref)を使用して、base_refが実際の派生型と一致するかどうかを判断します。

_int main () {
    base* pd = new derived();
    derived& x = dynamic_cast<derived&>(*pd); // Will throw an exception if pd 
                                          // isn't a 'derived'
    x.fn();
    return 0;
}
_

または代わりに:

_int main () {
    base* pd = new derived();
    derived* x = dynamic_cast<derived*>(pd); // Will return nullptr if pd isn't
                                         // a 'derived'
    if(x) {
        x->fn();
    }
    else {
        // dynamic_cast<derived*> failed ...
    }
    return 0;
}
_

c ++ は派生クラスの共変戻り型をサポートしますが、他の回答が説明しているように、ここで基本クラス(pd->get_this())を呼び出すことによってそれを取得することはできません。

[〜#〜] rtti [〜#〜] 、例外処理、または必要なものを使用できない場合は、コンパイル時に型コンプライアンスをチェックするために static polymorphism を検討することもできます。タイトタイプバインディング(vtableオーバーヘッドなし)。

8

pdの静的タイプは_base *_です。したがって、コンパイラーがメンバー関数get_this()を検索すると、base::get_this()のみが検出されます。 base::get_this()の戻り値の型は_base&_であり、_derived&_に変換できません。したがって、エラー。

3
Phil Miller

C++は、共変リターン型をサポートしています。つまり、derivedポインタを介してbaseオブジェクトでget_this()を呼び出すと、呼び出されるのは派生の実装です。

ただし、これはbase::get_thisを呼び出すとderived&が得られるという意味ではありません。 base::get_thisの戻り値の型はbase&です。 derivedオブジェクトを取得する場合は、derivedポインタを介してget_thisを呼び出す必要があります(またはbase&derived&にダウンキャストします)。これが、Java、C++、D ...での戻り型共分散の仕組みであることに注意してください。

base* pbase = new base();
base* pderived = new derived();
derived* pderived2 = new derived();

base& a = pbase->get_this();        // call implementation in base, return base&
base& b = pderived->get_this();     // call implementation in derived, return base&
derived& c = pderived2->get_this(); // call implementation in derived, return derived&
1
log0

ワーキングドラフトC++標準(ここをクリック) のセクション10.3、パラグラフ8を参照して、Novelocratの回答に追加したいと思います。この場合、返されるポインタの静的型は、ではなくDerived *です。ベース*。基本的に、派生クラスへのポインタを介してget_this()を呼び出した場合、コンパイラエラーなしで正しい型を取得できます。

これは、標準からの引用と例(これも標準から)です。

D :: fの戻り値の型がB :: fの戻り値の型と異なる場合、D :: fの戻り値の型のクラス型は、D :: fの宣言の時点で完全であるか、クラスである必要があります。タイプD。オーバーライドされた関数がオーバーライドされた関数の最終的なオーバーライドとして呼び出されると、その結果は(静的に選択された)オーバーライドされた関数(5.2.2)によって返される型に変換されます。 【例:

class B { };
class D : private B { friend class Derived; };
struct Base {
    virtual void vf1();
    virtual void vf2();
    virtual void vf3();
    virtual B* vf4();
    virtual B* vf5();
    void f();
};

struct No_good : public Base {
    D* vf4(); // error: B (base class of D) inaccessible
};

class A;
struct Derived : public Base {
    void vf1(); // virtual and overrides Base::vf1()
    void vf2(int); // not virtual, hides Base::vf2()
    char vf3(); // error: invalid difference in return type only
    D* vf4(); // OK: returns pointer to derived class
    A* vf5(); // error: returns pointer to incomplete class
    void f();
};

void g() {
    Derived d;
    Base* bp = &d; // standard conversion:
    // Derived* to Base*
    bp->vf1(); // calls Derived::vf1()
    bp->vf2(); // calls Base::vf2()
    bp->f(); // calls Base::f() (not virtual)
    B* p = bp->vf4(); // calls Derived::pf() and converts the
    // result to B*
    Derived* dp = &d;
    D* q = dp->vf4(); // calls Derived::pf() and does not
    // convert the result to B*
    dp->vf2(); // ill-formed: argument mismatch
}
1
user955279

私は簡単な解決策を見つけましたが、可能であれば、マスターは次のことを評価します。

class base{ 
    type = 1;
    virtual int getType() final {
        return type;
    }
}

class derived1 : public base {
    derived1(){
        type = 2;
    }
}

このようにして、任意の派生クラスのメソッド 'int getType()'を呼び出すことができます。型はコンストラクターに設定されているため、誤動作のリスクはありません。使いやすさを向上させるために、事前定義された「タイプ」を作成しました。

使っていますが、MacGyveryかどうかわかりません!

0
Auradrummer