C++編で扱っている C++ は 2003年に登場した C++03 という、とても古いバージョンのものです。C++ はその後、C++11 -> C++14 -> C++17 -> C++20 -> C++23 と更新されています。
なかでも C++11 での更新は非常に大きなものであり、これから C++ の学習を始めるのなら、C++11 よりも古いバージョンを対象にするべきではありません。特に事情がないなら、新しい C++ を学んでください。 当サイトでは、C++14 をベースにした新C++編を作成中です。
この章の概要です。
bitset は、ビット配列を提供するクラステンプレート📘です。つまり、各要素が 0 か 1 にしかならない配列を表現できます。使用するには、<bitset> という名前の標準ヘッダをインクルードする必要があります。
bitset は、次のように定義されています。
namespace std {
template <size_t Bits>
class bitset {
};
}
テンプレート仮引数 Bits は、ノンタイプテンプレート仮引数(【言語解説】第22章)で、ここにビット配列のビット総数(サイズ)を指定します。たとえば、bitset<10>
とすることで、10個の bool値を持った配列を定義できます。このような仕組みであるため、bitset では動的メモリ割り当て📘は行われません。
ビット総数をテンプレート仮引数で指定しているため、後から変更できません。この点が、bitset の大きな制約となっています。もし、動的📘にビット総数を変更するような使い方が必要であれば、vector<bool>(第5章)を使用する方法があります(ただし、vector<bool> は普通の vector とは違うことに注意)。
動的な bitset を実装したものに、boost の dynamic_bitset があります。
また、たとえば、bitset<10>
と bitset<20>
は別の型であることにも注意してください。
bitset には、複数のコンストラクタが定義されているので、さまざまな方法でインスタンス化できます。
#include <bitset>
#include <string>
typedef std::bitset<10> TestBitset;
int main()
{
std::string s("10110110");
; // すべて 0
TestBitset bset1(0xFF); // 整数値のビット表現を使って初期化
TestBitset bset2(s); // 文字列による表現から初期化
TestBitset bset3(s, 4); // 文字列による表現から初期化。開始位置を指定。
TestBitset bset4(s, 4, 2); // 文字列による表現から初期化。開始位置と文字数を指定。
TestBitset bset5}
bset1 のように、デフォルトコンストラクタによって生成された場合、すべてのビットは 0 で初期化されます。
bset2 では、unsigned long型を1つ指定し、そのビット表現どおりのビット配列が作られます。上の例では「0xFF」を渡しているので、「11111111」というビット配列になります。
bset3、bset4、bset5 は、std::string型の文字列を指定し、その表現からビット配列を作ります。bset4 では開始位置を、bset5 ではさらに文字数を指定しています。なお、bitset のサイズに満たない場合、不足分は 0 で埋められ、bitset よりも長い場合は、余分な指定は無視されます。つまり、bset3 は「0010110110」、bset4 は「0000000110」、bset5 は「0000000001」になります。
この文字列には ‘0’ か ‘1’ だけが含まれていなければなりません。もし他の種類の文字が含まれていたら、std::invalid_argument例外(第17章)が送出されます。
ところで、bset3、bset4、bset5 で使われているコンストラクタは、テンプレートコンストラクタ(【言語解説】第25章)になっているため、たとえば「TestBitset bset3(“10110110”)」のように、const char*型の文字列を指定すると、コンパイルエラーになります。そのため、明示的に「TestBitset bset3(std::string(“10110110”))」といったように指定する必要があります。
bitset は、動的なメモリ確保を行っていないので、デストラクタ📘においても特に何も行われません。
bitset においての「サイズ」は、ビット総数のことを指します。
bitset<0> としていない限り、要素がないという状態はあり得ないので、emptyメンバ関数はありません。あるのは sizeメンバ関数だけです。
#include <bitset>
#include <iostream>
int main()
{
std::bitset<0> bset1;
std::bitset<10> bset2;
std::bitset<100> bset3;
std::cout << bset1.size() << "\n"
<< bset2.size() << "\n"
<< bset3.size() << std::endl;
}
実行結果:
0
10
100
bitset は、標準入出力ストリーム(第26章、【言語解説】第5章)と直接連携できるようになっています。具体的には、std::istream を使って直接入力することができ、std::ostream を使って直接出力できます。
#include <bitset>
#include <iostream>
typedef std::bitset<10> TestBitset;
int main()
{
;
TestBitset bset
std::cin >> bset;
std::cout << bset << std::endl;
}
実行結果:
1101 (←入力内容)
0000001101
入力に関しては、‘0’ か ‘1’ のいずれかの文字が登場すると、そこで読み取りが停止します。また、1文字も読み取れなかった場合は、使用した入力ストリームに std::ios::failbit が設定されます。
bitset内の特定のビットを設定するには、setメンバ関数か、[]演算子を使います。
#include <bitset>
#include <iostream>
typedef std::bitset<8> TestBitset;
int main()
{
(0xF0); // 11110000
TestBitset bset
.set(3); // 3ビット目を true に
bset.set(7, false); // 7ビット目を false に
bset[0] = true; // 0ビット目を true に
bset
std::cout << bset << std::endl;
}
実行結果:
01111001
setメンバ関数には3つのオーバーロード📘があり、そのうちの2種類を使用しています。第1引数に、対象のビット位置を指定します。第2引数がある場合は、第2引数に指定した bool型の値をセットし、無い場合は true をセットします。
[]演算子の場合は、[]内にビット位置を指定すると、そのビット位置を指す参照を返します(厳密にはやや違います。コラム参照)。そのため、=演算子を使って、bool値を与えてやれば、ビットを設定できます。
【上級】[]演算子が返すのは、bitset<>::reference型の一時オブジェクトです。ポインタにせよ、参照にせよ、指し示す先は「バイト」であり「ビット」ではないので、このように間に1つクラスを挟むことで、間接的に実現しています。
正常な範囲外のビット位置を指定した場合、setメンバ関数は std::out_of_range例外(第17章)を送出するのに対し、[]演算子は未定義の動作になります。
setメンバ関数のもう1つの形式を使うと、全ビットがすべて true になります。
#include <bitset>
#include <iostream>
typedef std::bitset<8> TestBitset;
int main()
{
(0xF0); // 11110000
TestBitset bset
.set();
bset
std::cout << bset << std::endl;
}
実行結果:
11111111
このように、setメンバ関数が、基本的には true を設定するものであり、引数が2つあるバージョンでだけ true か false かを選択できるのに対し、false を設定するための resetメンバ関数があります。こちらは、2つのオーバーロード形式があります。
#include <bitset>
#include <iostream>
typedef std::bitset<8> TestBitset;
int main()
{
(0xF0); // 11110000
TestBitset bset
.reset();
bsetstd::cout << bset << std::endl;
.set(); // すべて true
bset.reset(3);
bsetstd::cout << bset << std::endl;
}
実行結果:
00000000
11110111
引数のない resetメンバ関数は、全ビットを false に設定します。引数のある resetメンバ関数は、指定したビット位置だけを false に設定します。resetメンバ関数は、setメンバ関数と同様に、有効な範囲外のビット位置を指定すると、std::out_of_range例外を送出します。
bitset内の特定のビットの状態を取得するには、[]演算子か、testメンバ関数を使います。
#include <bitset>
#include <iostream>
typedef std::bitset<8> TestBitset;
int main()
{
(0xF0); // 11110000
TestBitset bset
std::cout << bset[0] << "\n"
<< bset[4] << "\n"
<< bset.test(7) << std::endl;
}
実行結果:
0
1
1
[]演算子の場合は、[]内にビット位置を指定すると、そのビット位置の値を返します。要素を設定する場合の []演算子と違い、取得の際の []演算子の戻り値は、単なる bool型の値です。
testメンバ関数の場合も基本的に同じです。引数はビット位置で、そのビット位置の値を返します。
両者の違いは、正常な範囲外のビット位置を指定した場合で、testメンバ関数は std::out_of_range例外(第17章)を送出するのに対し、[]演算子は未定義の動作になります。
bitset は、各種のビット単位の演算が簡単に行えるようになっています。
まず、&演算子による AND演算、|演算子による OR演算、~演算子による NOT演算、^演算子による XOR演算、<<演算子による左シフト演算、>>演算子による右シフト演算がそれぞれ可能です。また、~演算子以外は、複合代入演算子が使用できます。
#include <bitset>
#include <iostream>
typedef std::bitset<8> TestBitset;
int main()
{
(0xF0); // 11110000
TestBitset bset
&= 0xAA;
bset std::cout << bset << std::endl; // 10100000
|= 0x0F;
bset std::cout << bset << std::endl; // 10101111
^= 0xFF;
bset std::cout << bset << std::endl; // 01010000
= ~bset;
bset std::cout << bset << std::endl; // 10101111
<<= 4;
bset std::cout << bset << std::endl; // 11110000
>>= 2;
bset std::cout << bset << std::endl; // 00111100
}
実行結果:
10100000
10101111
01010000
10101111
11110000
00111100
また、flipメンバ関数を使用すると、任意のビット、あるいはすべてのビットを反転できます。
#include <bitset>
#include <iostream>
typedef std::bitset<8> TestBitset;
int main()
{
(0xF0); // 11110000
TestBitset bset
.flip();
bsetstd::cout << bset << std::endl; // 00001111
.flip(2);
bsetstd::cout << bset << std::endl; // 00001011
[5].flip();
bsetstd::cout << bset << std::endl; // 00101011
}
実行結果:
00001111
00001011
00101011
3つ目のパターンでは、[]演算子が返した参照に対して呼び出しています。この形の場合は、[]演算子の時点で、対象のビット位置が特定されているので、そのビットだけが反転します。
【上級】「要素の設定」の項のコラムにあるように、[]演算子が返しているのは、bitset<>::reference型の一時オブジェクトです。上記の例は、bitset<>::reference::flipメンバ関数を呼び出している訳です。
また、ビットの状態を調べるメンバ関数がいくつか用意されています。
countメンバ関数を使うと、値が 1 (true) になっているビットの総数を取得できます。
値が 1 (true) になっているビットがあるかどうかを、anyメンバ関数で調べられます。逆に、値が 1 (true) になっているビットがないかどうかを、noneメンバ関数で調べられます。
#include <bitset>
#include <iostream>
typedef std::bitset<8> TestBitset;
int main()
{
(0xF0); // 11110000
TestBitset bset
std::cout << std::boolalpha
<< bset.count() << "\n"
<< bset.any() << "\n"
<< bset.none() << std::endl;
.reset(); // 00000000
bset
std::cout << std::boolalpha
<< bset.count() << "\n"
<< bset.any() << "\n"
<< bset.none() << std::endl;
}
実行結果:
4
true
false
0
false
true
to_stringメンバ関数は、bitset が保持しているビット列を、文字列化して返します。
std::cout に、bitset のオブジェクトをそのまま渡すと、文字列化したビット列が出力されますが、これは、to_stringメンバ関数を呼び出すことで実現されています。
#include <bitset>
#include <iostream>
typedef std::bitset<8> TestBitset;
int main()
{
(0xF0); // 11110000
TestBitset bset
std::string s = bset.to_string<char, std::char_traits<char>, std::allocator<char> >();
std::cout << s << std::endl;
}
実行結果:
11110000
呼び出している箇所を見ると分かるように、テンプレート実引数の指定が必要です。to_stringメンバ関数は、関数テンプレート📘(メンバ関数テンプレート)であり、std::basic_stringクラステンプレートに指定するテンプレート実引数を、そのままの順で指定するようになっています。関数自体に引数がないので、テンプレート実引数を推測させることもできず、1つ1つ指定するしかありません(【言語解説】第9章)。そのため、正直、かなり使いづらいと言わざるを得ません。
また、bitset が保持しているビット列を、整数値で返す to_ulongメンバ関数があります。これは、名前のとおり、unsigned long型で返しますが、もし表現できないビット列だった場合には、std::overflow_error例外(第17章)が送出されます。
#include <bitset>
#include <iostream>
typedef std::bitset<8> TestBitset;
int main()
{
(0xF0); // 11110000
TestBitset bset
std::cout << bset.to_ulong() << std::endl;
}
実行結果:
240
問題① bitset を利用して、整数値を、2進数を文字列で表現したものに変換する関数を作成してください。
Programming Place Plus のトップページへ
はてなブックマーク に保存 | Pocket に保存 | Facebook でシェア |
X で ポスト/フォロー | LINE で送る | noteで書く |
![]() |
管理者情報 | プライバシーポリシー |