15

現在、非 RTTI プラットフォーム (Android) で多くの RTTI を使用するサードパーティ パッケージの統合に取り組んでいます。基本的に、私は独自の RTTI 実装を行いましたが、問題が発生しています。

問題は、すべてのクラスが同じ基本クラス (オブジェクト) から派生するため、多くのクラスがダイヤモンド継承の問題を抱えていることです。したがって、基本クラスから派生クラスにダウンキャストする場合は、使用する必要があります。 dynamic_cast - ただし、RTTI は利用できません! dynamic_cast を使用しない仮想継承がある場合、オブジェクトを親から子に変換するにはどうすればよいですか?

次のようになります。

class A 
{
public:
 virtual char* func() { return "A"; };
};
class B : public virtual A
{
public:
 //virtual char* func() { return "B"; };
};
class C : public virtual A 
{
public:
 //virtual char* func() { return "C"; };
};

class D : public B, public C 
{
public:
 //virtual char* func() { return "D"; };
};

D d;
A* pa = static_cast<A*>(&d);
D* pd = static_cast<D*>(pa); // can't do that! dynamic_cast does work though...

それらは私のエラーです:

エラー C2635: 'A*' を 'D*' に変換できません。仮想基本クラスからの変換が暗示されています

error C2440: 'initializing' : 'test_convert::A *' から 'test_convert::D *' に変換できません ベースから派生へのキャストには、dynamic_cast または static_cast が必要です

何か案は?

4

6 に答える 6

12

このキャストはdynamic_cast;でのみ実行できます。他のキャストはこれを行いません。

このタイプのキャストを実行する必要がないようにインターフェイスを設計できない場合は、キャスト機能をクラス階層の一部にするしかありません。

例 (恐ろしくハック)

class D;

class A
{
public:
    virtual D* GetDPtr() { return 0; }
};

class B : public virtual A
{
};

class C : public virtual A 
{
};

class D : public B, public C 
{
public:
    virtual D* GetDPtr() { return this; }
};
于 2010-07-27T21:07:17.160 に答える
5

AndroidRTTI をサポートしています。最新の NDK (少なくとも r5、最新は r6) が必要であり、デフォルトではなく GNU stdlibc++ に対してコンパイルする必要があります。

以前から、例外と rtti をサポートする CrystaX のリビルドがありました (r5a と r5b にはサポートがあったため、公式の NDK r5c までそれを使用する必要がありましたが、古い (2.3 より前の) システムではクラッシュしました)。

PS: ベンダが例外と rtti をサポートしていないのに、C++ をサポートしていると言うのは本当に禁止すべきです。なぜなら、C++ 標準の一部である標準ライブラリのほとんどはどちらかがなければ機能しないからです。さらに、それらをサポートしないのは、特に例外の場合はばかげています。例外があるコードは、ないコードよりも効率的だからです (例外的なケースを通知するために適切に使用されている場合)。

于 2011-08-19T10:07:53.190 に答える
3

ほとんどの場合、訪問者パターンを使用してダウンキャストを回避できます。dynamic_cast を回避するためにも使用できます。

いくつかの注意事項:

1) 問題のあるクラスを変更できる必要があります。
2) すべての派生クラスを知る必要がある場合があります。
3) オブジェクトは、少なくとも基本クラスから派生することがわかっている必要があります。完全に無関係な型をキャストすることはできません。(「基底クラスから派生クラスにダウンキャストしたい」ということは満たされているようです)

次の例では、テンプレートを使用しました。これらは簡単に取り除くことができますが、かなりの書き込み作業が必要になります。

class A;
class B;
class C;
class D;

// completely abstract Visitor-baseclass.
// each visit-method must return whether it handled the object
class Visitor
{ 
public:
    virtual bool visit(A&) = 0;
    virtual bool visit(B&) = 0;
    virtual bool visit(C&) = 0;
    virtual bool visit(D&) = 0;
};

class A
{
public:
    virtual const char* func() { return "A"; };
    virtual void accept(Visitor& visitor) { visitor.visit(*this); }
};
class B : public virtual A
{
public:
    virtual const char* func() { return "B"; };
    virtual void accept(Visitor& visitor) { visitor.visit(*this); }
};
class C : public virtual A
{
public:
    virtual const char* func() { return "C"; };
    virtual void accept(Visitor& visitor) { visitor.visit(*this); }
};
class D : public B, public C
{
public:
    virtual const char* func() { return "D"; };
    virtual void accept(Visitor& visitor) { visitor.visit(*this); }
};

// implementation-superclass for visitors: 
// each visit-method is implemented and calls the visit-method with the parent-type(s)
class InheritanceVisitor : public Visitor
{ 
    virtual bool visit(A& a) { return false; }
    virtual bool visit(B& b) { return visit(static_cast<A&>(b)); }
    virtual bool visit(C& c) { return visit(static_cast<A&>(c)); }
    virtual bool visit(D& d) { return visit(static_cast<B&>(d)) || visit(static_cast<C&>(d)); }
};

template<typename T> // T must derive from A
class DerivedCastVisitor : public InheritanceVisitor
{
public:
    DerivedCastVisitor(T*& casted) : m_casted(casted) {}
    virtual bool visit(T& t) 
    { m_casted = &t; return true; }
private:
    T*& m_casted;
};

// If obj is derived from type T, then obj is casted to T* and returned. 
// Else NULL is returned.
template<typename T> 
T* derived_cast(A* obj)
{
  T* t = NULL;
  if (obj) 
  {
    DerivedCastVisitor<T> visitor(t);
    obj->accept(visitor);
  }
  return t;
}

int main(int argc, char** argv)
{
  std::auto_ptr<A> a(new A);
  std::auto_ptr<A> b(new B);
  std::auto_ptr<A> c(new C);
  std::auto_ptr<A> d(new D);

  assert(derived_cast<A>(a.get()) != NULL); // a has exact type A
  assert(derived_cast<B>(b.get()) != NULL); // b has exact type B
  assert(derived_cast<A>(b.get()) != NULL); // b is derived of A
  assert(derived_cast<C>(b.get()) == NULL); // b is not derived of C
  assert(derived_cast<D>(d.get()) != NULL); // d has exact type D
  assert(derived_cast<B>(d.get()) != NULL); // d is derived of B 
  assert(derived_cast<C>(d.get()) != NULL); // d is derived of C, too
  assert(derived_cast<D>(c.get()) == NULL); // c is not derived of D

  return 0;
}
于 2010-07-28T12:41:59.993 に答える
1

the code:

template <typename E, typename T>
E& force_exact(const T& ref)
 {
   static const E* exact_obj;
   static const T& exact_obj_ref = *exact_obj;
   static const ptrdiff_t exact_offset = ...

doesn't work very well for me as static const E* exact_obj is zero, so static const T& exact_obj_ref = *exact_obj derefs zero, too, and thus static const ptrdiff_t exact_offset becomes also zero.

It seems to me that the derived class needs to be instantiated (which may be a problem for abstract classes...). So my code is:

template <typename D, typename B>
D & Cast2Derived(B & b)
{ static D d;
  static D * pD = & d;
  static B * pB = pD;
  static ptrdiff_t off = (char *) pB - (char *) pD;

  return * (D *) ((char *) & b - off);
} 

Tested under MSVC 2008, WinXP 32b.

Any comments / better solution(s) are welcome.

LuP

于 2011-08-19T09:37:59.623 に答える
0

仮想継承の問題は、基本クラスのアドレスが派生したアドレスと必ずしも同じではないことです。したがって、偶数reinterpret_castまたはvoid*キャストは役に立ちません。

を使用せずにこれを解決する 1 つの方法dynamic_castは、キャスト中にそれに応じてオブジェクト アドレスを変更するために、両方のポインター型 (正確な型と参照型) の間のオフセットを計算することです。

 template <typename E, typename T>
 E& force_exact(const T& ref)
 {
   static const E* exact_obj;
   static const T& exact_obj_ref = *exact_obj;
   static const ptrdiff_t exact_offset =
     (const char*)(void*)(&exact_obj_ref)
     - (const char*)(void*)(exact_obj);
   return *(E*)((char*)(&ref) - exact_offset);
 }
于 2011-05-12T22:07:57.730 に答える
-2

実行時に実行中のタイプ セーフであることを確認する別の方法がある限り、reinterpret_cast を使用してください。

基本的には C スタイルのキャストと同じなので、必要な場合にのみ使用してください。ただし、上記のコードをコンパイルできるようにします。

于 2010-07-28T02:18:00.580 に答える