0

この質問は、異なる戻り型を持つ派生クラスの仮想メソッドをオーバーライドすることに関するものです。次のコードの場合:

class father {
public:
  virtual father* ref() { return this; }
};

class child : public father {
  virtual child* ref() { return this; }
};

ポインタを直接取得しようとすると、g ++(F15、g ++ 4.5)は「父から子への無効な変換」を報告します

child m_child;
father* pf = &m_child;
child* pc = pf->ref();

子クラスのref()メソッドが使用されていることを理解しています。これは、おそらくコンパイル時の型の不一致にすぎません。

ただし、型キャストを明示的に使用せずにそれを行う方法はありますか?

追加の説明:コンパイラがこのエラーを報告する理由を理解しています。私が必要としているのは、ポインタを明示的に変換せずに派生オブジェクトのデータにアクセスできる、すぐに使えるものです。

父クラスは、さまざまな派生子オブジェクトをリストまたはベクトルに配置するために使用されるため、アイテムがリストまたはベクトルからフェッチされるときに、それがどの子クラスに属しているかを判断する方法はありません。

子クラスのタイプを記録して確認するための内部メソッドがあります。しかし、私はポインタを明示的に変換したくありません。

たとえば、私は次のようなことをしたいと思います。

// assuming pf is pointer pointed to an item fetch from a vector
switch(fp->get_type()) {
case child_type1: fp->ref()->list1.push(data); break;
case child_type2: fp->ref()->list2.push(data); break;
case child_type3: fp->ref()->list3.push(data); break;
}

今のところ、新しい変数を明示的に宣言するか、fpを適切な型に明示的に変換する必要があります。そのたびに、面倒で混乱を招く派生クラスのデータにアクセスする必要があります。

私が期待しているのは、いくつかのBoostライブラリが、私がまだ知らない別の方法で同様のことを実行できるのか、それともc ++ 11標準で許可されているのに、特別なコンパイルパラメータを設定する必要があるのか​​ということです。

4

3 に答える 3

2

簡単な答えは「いいえ」です。

型情報をbase()へのポインタとして格納することにより、型情報を破棄したときの(child*ではなくfather*) 余分な情報を失ってしまいました。refm_childpf

キャストなしでは不可能になる理由の1つは、次の例です。

class father {
public:
  virtual father* ref() { return this; }
};

class childA : public father {
  virtual childA* ref() { return this; }
};

class childB : public father {
  virtual childB* ref() { return this; }
};

void should_never_compile(int i)
{
   childA a;
   childB b;
   father pf;
   if( i ) { pf=&a; }
   else { pf=&b; }

   // This is evil and will not compile
   childA * pa = pf->ref();

   //But this is OK:
   childA * pa = dynamic_cast<childA*>(pf->ref() );
}

ダイナミックキャストなしでこれを実行可能にしたい場合は、ダイナミックキャストを非表示にすることができます(ただし、少し怖いです)

class father {
public:
  virtual father* ref() { return this; }
  template<typename T> T* as() { return dynamic_cast<T*>(ref()); }
};

child m_child;
father* pf = &m_child;
child* pc = pf->as<child>();
于 2012-04-04T13:41:03.227 に答える
1

あなたの関数は、あなたが期待することと、あなたが指示したことを行います。この線

child* pc = pf->ref();

が問題です。父へのポインターで関数「ref()」を呼び出すと、(型によって)父*が返されます。これを変換せずに子 * に割り当てます。その関数の実装は子 * を返しますが、それを呼び出した場所からは、それが父 * であることしか知らないため、その情報はわかりません。したがって、戻り値の型は父 * (クラスの父で示されているように) であり、それを変換せずに子 * に割り当てます。次のことができます。

child *pc = m_child.ref();
father *pf = m_child.ref();
father *pf2 = pf->ref();
于 2012-04-04T13:25:03.903 に答える
1

問題は、呼び出しpf->ref()でのタイプが次のとおりでrefあることですfather* (father::*)()

つまり、特定の署名を持つメソッドになるように静的refに解決されます。そして、このシグネチャは、.virtualfather*

したがって:

int main() {
  father f;
  father* pf = &f;

  child c;
  child* pc = &c;

  child* xf = pf->ref(); // compile-time failure
  child* xc = pc->ref(); // okay
}

問題は、一般に、インスタンスの動的タイプに依存して、father*を取得するかどうかを知ることができないことです。child*したがって、コンパイラは最悪のケースを想定しています。少なくともfather.

于 2012-04-04T13:26:23.123 に答える