2012-03-17 7 views
0

標準のレイアウトタイプのレイアウト要件が、コピー可能なタイプにも適用されないコンパイラはありますか?特に、型へのポインタがその最初のメンバへのポインタであるという批判的なルール(基本クラスは派生クラスの前に来ると考えられる)です。つまり、型のアドレスはその基本型と同じアドレスになります。些細なことは常に擬似標準レイアウトにコピーできますか?

コードでは、次のコードが実際には動作しない一般的なコンパイラはありますか?それは私の常識のように思えるので、C++ 11では標準化されていなかったことに驚きました。

struct base { int a; /* is a trivial class*/ }; 
struct derived : public base { int b; /*still a trivial class*/ } 

void copy(base * a, base * b, size_t len) 
{ 
    memcpy(a, b, len); 
} 

... 
derived d1, d2; 
copy(&d1, &d2, sizeof(derived)); 

私は確かにこれはGCCで働く、と私は(私が間違っているかもしれませんが)、それはMSVCで働くと信じて知っています。非歴史的なコンパイラでは、上記は意図したとおりに動作しませんか?


拡張例

上記の例では、根本的な問題を示しているが、そこに一つになるだろう意図を示さないことができます。ここには少し冗長な例があります。本質的に誰もがメッセージをキューに入れる "send"を呼び出すことができます。そして、後で何かが実際のタイプにキャストすることによって各メッセージをディスパッチします。

struct header { int len, id; } 
struct derived : public header { int other, fields; } 

void send(header * msg) 
{ 
    char * buffer = get_suitably_aligned_buffer(msg->len); 
    memcpy(buffer, msg, msg->len); 
} 

void dispatch(char * buffer) 
{ 
    header * msg = static_cast<header*>(buffer); 
    if(msg->id == derived_id) 
    handle_derived(static_cast<derived*>(msg)); 
} 


derived d; 
d.len = sizeof(d); 
d.id = deirved_id; 
send(&d); 

... 
char * buffer = get_the_buffer_again(); 
dispatch(buffer); 

まだ多くの側面は省略していますが、重要な部分が示されています。

+6

C++では 'memcpy'オブジェクトは「一般的な」ものだと思いますか? –

+3

なぜ私は代入演算子を使うだけで他のことをやってもらえないのですか?私はかなり多くのコードでそれを見てきたので、C++でmemcpyを見ることはめったにありません。 – 111111

+0

@ LightnessRacesinOrbitさらに、標準は 'memcpy'オブジェクトの規則を提供しますが、この正確な場合を保証するものではありません。 –

答えて

1

はい、人々は単一の継承が存在する限り、C++でこれを行っています。はい、本質的に妥当です。いいえ、それは標準によってサポートされていません。普遍的にサポートされていますか?おそらく、あなたはすでにそれがポイントではないことを知っているようです。この種の疑問は、標準化が排除すべきものです。

C++は、この問題を解決するために、より洗練されたものではありません。

問題は、派生クラスの非静的データメンバーが、ベースに直接スプライスされているかのように、ベースのメンバーの後に同じパディングを続ける必要がないことです。

しかし、共通の初期シーケンス(故意にを避けての継承を避ける)を持つ標準レイアウト構造のunionはこの保証を受けます。空の基本クラスを多重含まれている場合

struct header { int len, id; } 

union derived { 
    struct { 
     header h; 
     int payload; 
    } fmt1; 

    struct { 
     header h; // repetitive 
     double payload; 
    } fmt2; 

    // etc for all message types 
}; 

レイアウトは、実際に最初の非静的データメンバが空のベースクラスと同じタイプである場合は特に、異なっていてもよいです。理由継承(まだ)これを行うことはできませんおそらく彼らは空の基地についての特別なケースを書くことに疲れている。

+0

レイアウトは、代わりに(パディング経由で)fisrtメンバーとして擬似ベースを持つよりも異なるかもしれないが、私はそれで問題ないと思います。私はあなたが示す正確な理由で解決策を避けることを望んでいました、それはエレガントではありません。特に私は別の言語と通信しようとしていないので、これは厳密にはC++からC++です。ありがとう。 –

1

私はこれがGCCで動作することを知っていますが、MSVC(これは間違っているかもしれませんが)でも動作すると思います。

いいえ。あなたはがそれらのコンパイラでを壊さないいくつかの例を実行しました。それはと知り合いを「確かに」分けています。

未定義の動作は未定義です。 GCCの次のバージョンではコードが壊れる可能性があります。次のバージョンのVisual Studioがコードを壊す可能性があります。実際、のリリースでコンパイルするか、または特定の最適化を行うとコードが損なわれる可能性があります。

標準に従うことは、あなたが得るものについて何かを「確かに」知る唯一の方法です。あなたがやっていることは、実装定義の振る舞いではありません。 は定義されていませんの動作です。したがって、が表示されても、合理的な回答を得られるとは信じられません。

+0

はい、標準レイアウトは保証されていますが、私のタイプはたやすいコピーしかできないため、保証は成立しません。それは私が驚いている理由です。その違いは非常に小さいので、私はなぜ保証がないのか分かりません。 –

+0

@ edA-qamort-ora-y:あなたのタイプは標準レイアウトではありませんか? –

+0

この例は明らかに基本的なものであり、根底にある問題のみを意図しているわけではありません。私がchar *を使わない理由は、実際の 'copy'関数が' base'クラスポインタのデータを使用しているからです(私の場合、長さは実際にはメンバ変数です)。 –

関連する問題