ビットフィールド
明示的なサイズ(ビット単位)を持つクラスのデータメンバーを宣言します。隣接するビットフィールドメンバーは、個々のバイトを共有し、またその境界をまたぐようにパックされることがあります(またはされないこともあります)。
ビットフィールド宣言は、以下の宣言子を使用するクラスのデータメンバー宣言です。
identifier(optional) attr(optional) : size |
(1) | ||||||||
identifier(optional) attr(optional) : size brace-or-equal-initializer |
(2) | (C++20以降) | |||||||
ビットフィールドの型は、宣言構文のdecl-specifier-seqによって導入されます。
| attr | - | (C++11以降) 任意の数の属性のシーケンス |
| identifier | - | 宣言されるビットフィールドの名前。名前は省略可能です。名前のないビットフィールドは、指定された数のパディングビットを導入します。 |
| size | - | 0以上の値を持つ整数定数式。0より大きい場合、これはこのビットフィールドが占めるビット数です。値0は名前のないビットフィールドにのみ許可され、特別な意味を持ちます。 |
| brace-or-equal-initializer | - | このビットフィールドで使用されるデフォルトメンバー初期化子 |
目次 |
[編集] 説明
ビットフィールドの型は、整数型(boolを含む)または(cv修飾されている可能性のある)列挙型のみであり、名前のないビットフィールドをcv修飾された型で宣言することはできません。
ビットフィールドは静的データメンバーにすることはできません。
ビットフィールドのprvalueはありません。lvalue-to-rvalue変換は常にビットフィールドの基底型のオブジェクトを生成します。
ビットフィールドのビット数は、保持できる値の範囲に制限を設定します。
#include <iostream> struct S { // three-bit unsigned field, allowed values are 0...7 unsigned int b : 3; }; int main() { S s = {6}; ++s.b; // store the value 7 in the bit-field std::cout << s.b << '\n'; ++s.b; // the value 8 does not fit in this bit-field std::cout << s.b << '\n'; // formally implementation-defined, typically 0 }
実行結果の例
7 0
複数の隣接するビットフィールドは通常、まとめてパックされます(ただし、この動作は実装定義です)。
#include <bit> #include <cstdint> #include <iostream> struct S { // will usually occupy 2 bytes: unsigned char b1 : 3; // 1st 3 bits (in 1st byte) are b1 unsigned char : 2; // next 2 bits (in 1st byte) are blocked out as unused unsigned char b2 : 6; // 6 bits for b2 - doesn't fit into the 1st byte => starts a 2nd unsigned char b3 : 2; // 2 bits for b3 - next (and final) bits in the 2nd byte }; int main() { std::cout << sizeof(S) << '\n'; // usually prints 2 S s; // set distinguishable field values s.b1 = 0b111; s.b2 = 0b101111; s.b3 = 0b11; // show layout of fields in S auto i = std::bit_cast<std::uint16_t>(s); // usually prints 1110000011110111 // breakdown is: └┬┘├┘└┬┘└─┬──┘└┤ // b1 u a b2 b3 // where “u” marks the unused :2 specified in the struct, and // “a” marks compiler-added padding to byte-align the next field. // Byte-alignment is happening because b2's type is declared unsigned char; // if b2 were declared uint16_t there would be no “a”, b2 would abut “u”. for (auto b = i; b; b >>= 1) // print LSB-first std::cout << (b & 1); std::cout << '\n'; }
実行結果の例
2 1110000011110111
サイズが0の特別な名前のないビットフィールドは、パディングを強制的に分割することができます。これは、次のビットフィールドがその割り当て単位の先頭から始まることを指定します。
#include <iostream> struct S { // will usually occupy 2 bytes: // 3 bits: value of b1 // 5 bits: unused // 2 bits: value of b2 // 6 bits: unused unsigned char b1 : 3; unsigned char :0; // start a new byte unsigned char b2 : 2; }; int main() { std::cout << sizeof(S) << '\n'; // usually prints 2 // would usually print 1 if not for // the padding break in line 11 }
実行結果の例
2
ビットフィールドの指定されたサイズがその型のサイズより大きい場合、値は型によって制限されます。たとえば、std::uint8_t b : 1000;は、依然として[0, 255]の範囲の値を保持します。余分なビットはパディングビットです。
ビットフィールドは必ずしもバイトの先頭から始まるわけではないため、ビットフィールドのアドレスを取ることはできません。ビットフィールドへのポインタや非const参照は不可能です。const参照をビットフィールドから初期化する場合、一時オブジェクトが作成され(その型はビットフィールドの型です)、ビットフィールドの値でコピー初期化され、参照はその一時オブジェクトにバインドされます。
|
ビットフィールドにはデフォルトメンバー初期化子はありません。int b : 1 = 0;とint b : 1 {0}は不正です。 |
(C++20まで) |
|
ビットフィールドのサイズとデフォルトメンバー初期化子の間に曖昧さがある場合、有効なサイズを形成する最長のトークンシーケンスが選択されます。 int a; const int b = 0; struct S { // simple cases int x1 : 8 = 42; // OK; "= 42" is brace-or-equal-initializer int x2 : 8 {42}; // OK; "{42}" is brace-or-equal-initializer // ambiguities int y1 : true ? 8 : a = 42; // OK; brace-or-equal-initializer is absent int y2 : true ? 8 : b = 42; // error: cannot assign to const int int y3 : (true ? 8 : b) = 42; // OK; "= 42" is brace-or-equal-initializer int z : 1 || new int{0}; // OK; brace-or-equal-initializer is absent }; |
(C++20以降) |
[編集] 注釈
ビットフィールドの以下のプロパティは実装定義です。
- 符号付きビットフィールドに範囲外の値を代入または初期化した場合、または符号付きビットフィールドがその範囲を超えてインクリメントされた場合に生じる値。
- クラスオブジェクト内のビットフィールドの実際の割り当ての詳細に関するすべて。
- たとえば、一部のプラットフォームでは、ビットフィールドはバイトをまたがず、他のプラットフォームではまたがります。
- また、一部のプラットフォームでは、ビットフィールドは左から右にパックされ、他のプラットフォームでは右から左にパックされます。
Cプログラミング言語では、ビットフィールドの幅は基底型の幅を超えることはできず、明示的にsignedまたはunsignedとして宣言されていないintビットフィールドが符号付きか符号なしかは実装定義です。たとえば、int b : 3;はCでは[0, 7]または[-4, 3]の範囲の値を持つことができますが、C++では後者の選択肢のみが許可されています。
[編集] 欠陥報告
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| CWG 324 | C++98 | ビットフィールドへの代入の 戻り値がビットフィールドであるかどうかが未指定だった。 |
lvalueを返す可能性のある演算子の ビットフィールド仕様を追加。 |
| CWG 739 | C++98 | signedもunsignedも宣言されていないビットフィールドの 符号付きかどうかが実装定義だった。 |
基底型と整合するように。 |
| CWG 2229 | C++98 | 名前のないビットフィールドをcv修飾型で宣言できた。 | 禁止された |
| CWG 2511 | C++98 | ビットフィールド型でcv修飾が許可されていなかった。 | ビットフィールドはcv修飾を持つことができる。 列挙型 |
[編集] 参照
- C++23標準 (ISO/IEC 14882:2024)
- 11.4.10 ビットフィールド [class.bit]
- C++20 standard (ISO/IEC 14882:2020)
- 11.4.9 ビットフィールド [class.bit]
- C++17 standard (ISO/IEC 14882:2017)
- 12.2.4 ビットフィールド [class.bit]
- C++14 standard (ISO/IEC 14882:2014)
- 9.6 ビットフィールド [class.bit]
- C++11 standard (ISO/IEC 14882:2011)
- 9.6 ビットフィールド [class.bit]
- C++03 標準 (ISO/IEC 14882:2003)
- 9.6 ビットフィールド [class.bit]
- C++98 標準 (ISO/IEC 14882:1998)
- 9.6 ビットフィールド [class.bit]
[編集] 関連項目
| 固定長のビット配列を実装する (クラステンプレート) | |
| 空間効率の良い動的ビットセット (class template specialization) | |
| ビット操作 (C++20) | 個々のビットとビットシーケンスにアクセス、操作、処理するためのユーティリティ。 |
| ビットフィールドに関するCドキュメント
| |