2011-01-03 13 views
1

私は最近、C++の知識を更新しています。厳密なエイリアシングについて学ぶことで、あるタイプのポインタを別のタイプのポインタにキャストしようとしています。私はこの次のサンプル・コードでは、私のコンパイラ上で実際に動作することを知っているが、私は現在の規格には適合していることを確認する:基本的に、この単純なタプルは、要素のペアが含まれてい このインスタンスにメンバ変数へのポインタをキャストしても問題ありませんか?

#include <iostream> 

using namespace std; 

class MyBase { 

    public: 
    virtual void DoSomething() = 0; 

}; 

class MyDerived1 : public MyBase { 

    public: 
    virtual void DoSomething() { 
     cout << "I'm #1" << endl; 
    } 

}; 

class MyDerived2 : public MyBase { 

    public: 
    virtual void DoSomething() { 
     cout << "I'm #2" << endl; 
    } 

}; 

template <typename Base, typename Member1, typename Member2> 
struct Tuple { 

    public: 
    Base* Get(int i) { 
     return &(this->*(lookupTable[i])); 
    } 

    private: 
    Member1 member1; 
    Member2 member2; 

    static Base Tuple::* const lookupTable[2]; 

}; 

template <typename Base, typename Member1, typename Member2> 
Base Tuple<Base, Member1, Member2>::* const Tuple<Base, Member1, Member2>::lookupTable[2] = { 
    reinterpret_cast<Base Tuple<Base, Member1, Member2>::*>(&Tuple::member1), 
    reinterpret_cast<Base Tuple<Base, Member1, Member2>::*>(&Tuple::member2) 
}; 

int main() { 

    Tuple<MyBase, MyDerived1, MyDerived2> tuple; 

    tuple.Get(0)->DoSomething(); 
    tuple.Get(1)->DoSomething(); 

    return 0; 

} 

、派生する必要があり、それぞれが共通の基底クラスから。 Get関数は、指定されたインデックスが表すメンバに Base*を返します。

私が思っている重要な部分は、reinterpret_castsです。 Derived Struct::*からBase Struct::*へのキャストはですが、通常はですが、この場合はオブジェクトへのポインタがのポインタを取得するためにポインタのみを使用します。 (派生オブジェクトを基底オブジェクトとしてコピーしたり、基底オブジェクトを派生オブジェクトのメモリに入れることはしません)。これはG ++で意図されたとおりに動作し、私は確信していませんこれを行うための準拠したコンパイラに噛まれることになります。

答えて

1

reinterpret_castの使用はほとんど不可能です。その上で、メンバーのキャストへのポインタのだけ有効な使用がType Base::*Type Derived::*からの暗黙的なキャストとType Base::*からType Derived::*static_castの慎重な使用されています。メンバーを含むオブジェクトのタイプではなく、メンバーのタイプを変更したいので、これはそれらのどちらでもありません。

メンバへのポインタの代わりにその配列に小さな関数を置くのはどうですか?次のコードはテスト済みで、完全に移植可能でなければなりません。

#include <iostream> 

using namespace std; 

class MyBase { 

    public: 
    virtual void DoSomething() = 0; 

}; 

class MyDerived1 : public MyBase { 

    public: 
    virtual void DoSomething() { 
     cout << "I'm #1" << endl; 
    } 

}; 

class MyDerived2 : public MyBase { 

    public: 
    virtual void DoSomething() { 
     cout << "I'm #2" << endl; 
    } 

}; 

template <typename Base, typename Member1, typename Member2> 
struct Tuple { 

    public: 
    Base* Get(int i) { 
     return &(this->*lookupTable[i])(); 
    } 

    private: 
    Member1 member1; 
    Member2 member2; 

    template <typename MemType, MemType Tuple::*member> 
    Base& GetMember() { return this->*member; } 

    typedef Base& (Tuple::*get_member_func)(); 
    static const get_member_func lookupTable[2]; 

}; 

template <typename Base, typename Member1, typename Member2> 
const typename Tuple<Base, Member1, Member2>::get_member_func 
Tuple<Base, Member1, Member2>::lookupTable[2] = { 
    &Tuple::GetMember<Member1, &Tuple::member1>, 
    &Tuple::GetMember<Member2, &Tuple::member2> 
}; 

int main() { 

    Tuple<MyBase, MyDerived1, MyDerived2> tuple; 

    tuple.Get(0)->DoSomething(); 
    tuple.Get(1)->DoSomething(); 

    return 0; 

} 
+0

私はこの考えも持っていましたが、この複雑な設定の主な理由はできるだけ少ない数の関数呼び出しを試みることです。私は最後の手段としてこれを行うことができますが、私は誰かが関数呼び出しを必要とせずにこれを達成する(準拠した)方法を知っていることを期待しています。 – nonoitall

2

ここにはreinterpret_castを使用しないでください。実際には、reinterpret_castの使用方法については、どこの目的が移植性であるかは言及しないでください。 reinterpret_castは、プラットフォーム固有の結果を持つものです。

派生クラスのポインタに基底へのポインタをキャストする場合、dynamic_castを使用すると、指し示されたオブジェクトが派生クラスでない場合はNULLが返されます。クラスが正しいことを絶対に確信しているのであれば、static_castを使うことができます。

+1

"reinterpret_cast"の結果は、プラットフォームによって異なる "定義によって"ではありません。他の名前ではプラットフォーム固有でもありません。しかし、型安全性を捨てるので危険です。結果は悲惨であり、コードの保守は不安定になる可能性があります。しかしそうでなければ、代わりにdynamic_castを使用する必要があります。 –

+0

dynamic_castは、これらのポインタがオブジェクトを指していないため、ここでは機能しません。オブジェクトは相対メンバのオフセットを指しています。キャストが使用される時点では、動的キャストを使用するRTTIはありません。 static_castは、ポインタ型が技術的に互換性がないため動作しません。 – nonoitall

+2

標準を読んだ場合、reinterpret_castが定義されている場合にのみ、reinterpret_cast以前のものからその型に戻すことができます。ケースの残りはプラットフォーム固有のものです。 –

0

EDIT:標準からの参照。私が正しく読んでいれば、どちらの例外も満たさないので、何をしているのかは不明ですし、特定のコンパイラではうまく動作しないかもしれません。関連するメンバーのタイプには例外がありません。

5.2.10/9(reinterpret_castは)より:

「タイプT1のXのメンバー へのポインタ」型の右辺値を明示的 部材にタイプ 「ポインタの右辺値に変換することができます。 T1とT2の両方が機能型であるか、または 両方のオブジェクト型の場合は、0,ポインター値(4.11)は 宛先タイプのNULLメンバーポインター値に変換されます。この 変換の結果は 以下の場合を除き、指定されていない:

- 戻す 元の型に メンバ関数型とは異なるポインタに型「部材 関数へのポインタ」の 右辺値を変換し元の メンバー値へのポインタを返します。

- アラインメント要件T2の は厳密ではありません(「タイプ T2のYのデータメンバへのポインタ」型 のタイプの右辺値 変換「データ型T1のXの メンバへのポインタ」 T1のものより)、元のタイプの に戻すと、元のポインタ 値が返されます。

+0

MyDerived1を選択した場合、メンバポインタをMyDerived2にMyDerived1に間違ってキャストしなければなりません。逆も同様に適用されます。 – nonoitall

-1

Cスタイルのキャストは常にreinterpret_castより優れています。

Cスタイルのキャストが動作する場合、それは独立して有効なプラットフォームです。

reinterpret_castを常に避けてください。

EDITED

私はあなたが間違ったメモリアドレスを指し示すことができreinterpret_castはと、CスタイルのキャストがなどABI、メモリ整列、ポインタのサイズ、

編集され、すべてのプラットフォーム関連の問題を扱う、意味 解説者のインスピレーションによって、私はISO/IEC 14882:2003セクション5.2.10 "Reinterpret_cast"を読みました。

私の理解は限られていますが、なぜ私が最初にreinterpret_castを嫌ったのかを覚えています。

私は、reinterpret_castは継承階層の欠如しているか、または非常に限定された意識を持っていると思います。

キャスト・オペランドが複雑な継承階層(ATL/COMクラスなど)を持つクラスのインスタンス・ポインタの場合、reinterpret_castは理解できないエラーで処理を停止するのに十分です。

実際のキャスト操作のあいまいな知識があるCスタイルキャストを使用できます。しかし、reinterpret_castを安全に使用するには、正確な詳細を正確に把握しておく必要があります。

+0

その場合、reinterpret_castの目的は何ですか? – nonoitall

+0

元のアイデアは、Cスタイルのキャストがキッチンシンク/スイス軍ナイフのビットであるため、キャストをインテントをより明確にすることでした。つまり、コンバージョンと再解釈の違いを理解することさえできません。残念なことに、標準のキャストはスペースを適切に分割できず、再解釈キャストを制限する少なくとも1つの不条理なルールが含まれます(論理的に制限がない場合)。それは残念だ、そのアイデアは良かった。 – Yttrill

+0

reinterpret_castはこれです: "私はマスターです!コンパイラが落ちます!私は何をやっているのか知っています!"私たちのような普通の人にとっては本当に危険です。 – 9dan

関連する問題