2013-07-12 20 views
10

C++ 11のセクション9.6/3は、「非const参照はビットフィールドにバインドされてはなりません。この禁止の背後にある動機は何ですか?ビットフィールドへの非const参照が禁止されているのはなぜですか?

私は、ビットフィールドへの参照を直接バインドすることはできないことを理解します。しかし、このような宣言をすると、

struct IPv4Header { 
    std::uint32_t version:4,   // assumes the IPv4 Wikipedia entry is correct 
       IHL:4, 
       DSCP:6, 
       ECN:2, 
       totalLength:16; 
}; 

なぜ私はこれを言うことができないのですか?

IPv4Header h; 

auto& ecn = h.ECN; 

は、私は、基礎となるコードは実際に私が興味のビットが含まれています全体std::uint32_tに特異的に結合することを期待したい、と私は、読み取りおよび書き込み操作が適切なマスキングを行うためのコードを生成することを期待したいです。結果は大きくて遅いかもしれませんが、それはうまくいくはずです。タイプCONSTの参照 T &の初期化子は、ビットを指す左辺値である場合

:これは標準constビットフィールドへの参照は、(再び9.6/3から)動作することを言う方法と一致するであろう-field、参照はビットフィールドの値を保持する に初期化された一時的にバインドされています。参照はビットフィールドに直接バインドされません。

これはビットフィールドへの書き込みが問題であることを示していますが、私はそれが何かを見ていません。私は、必要なマスキングがマルチスレッドコードにレースを導入する可能性を考えましたが、1.7/3あたり、非ゼロ幅の隣接ビットフィールドはマルチスレッドの目的で単一のオブジェクトとみなされます。上記の例では、IPv4Headerオブジェクト内のすべてのビットフィールドは単一のオブジェクトと見なされるため、他のフィールドを読み取っている間にフィールドを変更しようとするマルチスレッドコードは、定義上既にレイシーになっています。

私は明らかに何かが不足しています。それは何ですか?

答えて

7

同じ理由で非const参照をビットフィールドにバインドすることはできません。ポインタはビットフィールドを指すことができません。

参照が記憶域を占有するかどうかは指定されていませんが、自明でない場合はポインタを偽装として実装されており、この参照の実装は言語の作者によって「意図された」ものです。ポインタと同様に、参照はアドレス指定可能な記憶装置を指し示す必要があります。非const参照をアドレス可能ではない記憶装置にバインドすることは不可能である。非const参照は直接バインディングを必要とするため、非const参照をビットフィールドにバインドすることはできません。

ビットフィールドを指すポインタ/参照を生成する唯一の方法は、記憶装置の実際のアドレスに加えて何らかの種類のビットオフセットとビットを含む「スーパーポインタ」を実装することですどのビットを変更するかを書いているコードに伝えるために、-width情報を使用します。この追加情報は、すべてのデータポインタ型に存在しなければならないことに注意してください。これは、C++では「ポインタ/ビットフィールドへの参照」という型がないためです。これは、基礎となるOS /ハードウェアプラットフォームによって提供されるアドレッシングモデルからかなり離れた、より高度なストレージアドレッシングモデルを実装することと基本的に同じです。 C++言語は、純粋な効率の考慮から、そのような抽象化を基になるプラットフォームから要求することを決して意図していませんでした。

実行可能なアプローチの1つは、通常のデータポインタ/参照よりも複雑な内部構造を持つ "ポインタ/ビットフィールドへの参照"のような別のカテゴリのポインタ/参照を導入することです。そのような型は、通常のデータポインタ/参照型から変換可能ですが、逆もありません。しかし、それはそれに値するとは思われません。

ビットとビットシーケンスにパックされたデータに対処する必要がある場合、ビットフィールドを手動で実装し、言語レベルのビットフィールドを避けることがよくあります。ビットフィールドの名前は、あらゆる種類の実行時選択の可能性のないコンパイル時エンティティです。実行時の選択が必要な場合、より良い方法は、通常のuint32_tデータフィールドを宣言し、その中の個々のビットとビットグループを手動で管理することです。このような手動の「ビットフィールド」のランタイム選択は、マスクとシフト(どちらもランタイム値とすることができます)を使用して簡単に実装されます。基本的には、これは前述の「スーパーポインタ」の手動実装に近いです。

+0

私はこれを答えとしてマークしています。なぜなら、私が主張していることを明白にしているからです。ビットフィールドを持つ単語への参照が、私がスケッチしたやり方で動作するならば、ビットフィールドの単語へのオフセット。C++で採用されているリファレンス参照ポインタの場合は、実際には実装できません。 – KnowItAllWannabe

9

あなたは&とそのアドレスを取ることができないのと同じ理由のためのビットフィールドへの非constの参照を取ることができない:その実際のアドレスは、必ずしもcharに整列されていない、definitionallyメモリのアドレス可能な最小単位でありますC + +抽象マシンで。コンパイラがコピーに変更されていないので、constの参照を取ることができます。

別々のコンパイルの問題を考慮してください。 const uint32_t&をとる関数は、const uint32_t&で操作するのに同じコードを使用する必要があります。通常の値とビットフィールド値に異なる書き込み動作が必要な場合、その型は関数が両方で正しく機能するための十分な情報をエンコードしません。

+0

これは本当にIMOの質問には答えません。非コンス(non-const)参照がビットフィールドを含む単語にバインドできないのはなぜですか?次に、書き込みでビットフィールドのビットだけを変更するのに必要なマスキングを実行しますか?これはおそらくビットフィールドが直接変更されたときに何が起こるのでしょうか? – KnowItAllWannabe

+1

最後のビットが間違っています。 const参照は、値が変更されているかどうかについては何も意味しません。それは、*その*参照を通して突然変異を防ぐだけです。 –

+1

@AndreyTそれでも、その言葉が正しいことはありません。コンパイラは変更されないため、自由にコピーすることはできません。ここに因果関係はありません。コンパイラは命令*によってそれを自由にコピーすることができます*。それはビットフィールドの変更のためではありません、それはそう書かれているからです。実際には、それをコピーするのは実際には無料ではありません。コピーする必要があります。 –

関連する問題