Document directory
- Bitset
- Vector <bool>
- Summary
- Listing 1-bitset_iterator, an iterator adaptor class for STD: bitset
Cuj: Standard Library: bitset and bit vector)
The standard librarian: bitsets and bit Vectors
Matt austern
Http://www.cuj.com/experts/1905/austern.htm? Topic = experts
In C ++, you can play with bitmoney as expected, and you don't even need macros.
------------------------------------------------------------------------------
People who are familiar with the coding process are familiar with the Boolean option flag: process a set of options into a whole, package them into a word, and use a single bit for each option. For example, to set the permission for a UNIX file, you may write something like this:
Chmod ("my_file ",
S_iwusr | s_irusr |
S_irgrp | s_iroth );
Each constant corresponds to a single digit. By combining them with "bits or" operations, you can specify many options at a time.
It is common to Package Multiple option bits into a word. This technique is used in many places, in UNIX and Win32 APIs, In the ios_base formatting flag of the C ++ standard Runtime Library, and some forms of it are easy to appear in large programs. A collection of bitwise elements is very important.
It is not hard to understand why this technique is common: Another method is to use arrays or structures, and each option corresponds to a different field, which is clumsy and a waste of memory. However, this technique sometimes causes trouble. First, some operations may be clumsy: Set a bit named directly (flags | = s_irgrp), but clear a single bit (flages & = ~ S_iwgrp) somewhat ugly. You can test whether a single position is set. Mask it by: If (falgs & s_iwusr); but be careful when making an "Explicit" test error: If (flags & s_iwusr) = true), or worse if (flags & s_iwusr = ture ). It corresponds to the named bits. For the numbered bits, it is also clumsy: You need to use a bit similar to flags & = ~ (1 <n) is usually forced type conversion. Finally, this technique is hard to cover with many options: once the number of signs exceeds the number of long digits, you need to find another path.
Because the collection of BITs is very important, the C ++ standard Runtime Library provides explicit support for them-in fact, there are several types of support. Sometimes you will still want to use low-level bitwise operations (and sometimes you have to do this if you are interacting with the C language API), but in most cases, the version in the C ++ Runtime Library is more suitable. They have some minor problems, but most of them are easy to bypass.
Bitset
STD: bitset appears in chapter 23rd "associated container" of C ++ standard. This is not the correct location for it, because bitset has nothing to do with associated containers such as set and map, and does not even meet the most basic requirements of STL containers. It would be better to treat bitset as an integer, and each bit of bitset can be accessed independently-but it is not limited by the length of long. The length of a bitset is determined during the compilation period (the number of BITs is a template parameter), but there is no upper limit: bitset <32> is 32-bit long, And bitset <1000> is 1000.
The integer bit operation you have used is still valid for bistset. For convenience, you have added some operations. For example, you can write B1 ^ B2 to perform a bitwise OR operation (at least at the same length as B1 and B2 ). Operations on a single bit have two different interfaces: You can use B. set (n) to set the n-digit. Use B. reset (n) clears it and uses if (B. test (N) to test it. Alternatively, you can use bitset as an array and use B [N] = true, B [N] = false, and if (B [N. ("Almost", because there is a small difference: the array version does not perform cross-border checks, but the Set ()/RESET ()/test () version does. If the parameter passed to set ()/RESET ()/test () is too large, an exception occurs in out_of_range .)
If the bitset size you use is appropriate, Intuitively you can treat it as an integer: There is a constructor to create a bitset from unsigned long, and a member function to_ulong () to obtain an unsigned long from the bitset. Of course, you cannot directly use this constructor to initialize the bits that exceed the unsigned long range. Similarly, you cannot use to_ulong () to extract the bits that exceed the unsigned long range. (If you try to do this and any bit that exceeds unsigned long is set, to_ulong () throws an exception ). However, if necessary, you can bypass these limitations by Using Shift and mask:
Const int n =
Sizeof (unsigned long) * char_bit;
Unsigned long high = 0x7b62;
Unsigned long low = 0x1430;
STD: bitset <2 * n> B
= (STD: bitset <2 * n> (high) <n) |
STD: bitset <2 * n> (low );
...
Const STD: bitset <2 * n>
Mask (unsigned long) (-1 ));
Low = (B & Mask). to_ulong ();
High = (B> N). to_ulong ();
The 0th bits are defined as the lowest valid bits. Therefore, for example, if you write:
STD: bitset <4> B (0xa );
The positions to be set are B [1] and B [3].
It is easy to use bitset to replace the traditional option flag: you only need to declare a bitset object in the header file to replace the integer constant. We have already mentioned two advantages of using bitset: You get more signs than long can represent, and you can operate each bit in easier and safer ways. Another is bitset, which provides a conversion mechanism for Bidirectional conversion between bitset and text expressions.
First, bitset provides common I/O operations. This program,
# Include <bitset>
# Include <iostream>
Int main (){
STD: bitset <12> B (3432 );
STD: cout <"3432 In binary is"
<B <STD: Endl;
}
Intuitive results are provided:
3432. In binary is 110101101000.
Input operations work in the same way: It reads a string consisting of "1" and "0" and converts them into a bitset.
Second, you can convert bitsets into strings or strings: There is a constructor that accepts a string parameter, and bitset <>:: to_string () member function. Alas, although these conversions are useful, the details indicate that they are very inconvenient. The constructor that accepts string and the to_string () member functions are both Member templates, because the STD: basic_string class of the Runtime Library itself is a template; the common string class, STD :: string is an alias of basic_string <char>.
The versatility of these member templates is unfortunately influenced by some obscure rules of C ++. You must write:
STD: bitset <6> (STD: string ("110101 "));
Instead
STD: bitset <6> ("110101 ");
Only the version directly passed in by the string text "110101" will give a compilation error because the compiler does not know the version of the member template. Similarly, if B is a bitset, you cannot simply write:
STD: String S = B. to_string ();
You must switch to this form of terror:
STD: String s
= B. template to_string <char,
STD: char_traits <char>,
STD: Allocator <char> ();
(Yes, the seemingly ridiculous template keyword is really required .)
Of course, in practice, you should not use such a thing to pollute your code. Unless you really need to work with multiple character types, you can encapsulate horrible syntax details into helper functions:
Template <STD: size_t n>
STD: bitset <n>
From_string (const STD: string & S ){
Return STD: bitset <n> (s );
}
Template <STD: size_t n>
STD: String
To_string (const STD: bitset <n> & B ){
Return B. template to_string <char,
STD: char_traits <char>,
STD: Allocator <char> ();
}
Vector <bool>
Bitset does have an important limit: it has a fixed length. You can have a bitset that is longer than long, but you must specify its size in advance. This is good for options like flag set, but it is not suitable for other purposes. Assume that you are dealing with a huge set of terms in a complicated order, and you need to know what you have already seen. This requires a Boolean array. There is a reason to use a "COMPRESSED" array. Each element uses a single bit, but bitset is no longer a reasonable choice. The number of terms you are dealing with will not be known until runtime, And the terms may even be added or removed.
Another management Bit Set Mechanism in the C ++ standard Runtime Library is vector <bool> and vector <> A special template. In some ways, vector <bool> is very similar to bitset: each element is represented by a single bit, allowing you to use the array syntax (for example, V [3] = true) to access a single bit. The difference is that bitset uses its own special mechanism, while vector <bool> uses the common STL interface. You can use the resize () member function to change the number of elements, or use push_back () to add new elements, just like other vectors <t>.
Although vector <bool> does not provide special support for bitwise operations, you can still perform these operations using common STL generic algorithms and functor. For example, if you do not write V3 = V1 & V2, you can write:
STD: vector <bool> V3 (v1.size ());
STD: Transform (v1.begin (), v1.end (),
V2.begin (), v3.begin (),
STD: logical_and <bool> ());
Similarly, output vector <bool> in the same format as bitset operator <. You can use an STL generic algorithm again:
STD: Copy (V. rbegin (), V. rend (),
STD: ostream_iterator <char> (STD: cout ));
(This Code depends on a fact. By default, bool outputs "1" and "0" instead of "true" and "false ". We also noticed that we are using rbegin () and rend () to copy vector <bool> in reverse order. This is how bitset outputs: the leftmost Number of bitset prints is B [N-1], not B [0].)
Whenever possible, you should always use bitset instead of vector <bool>: a fixed-size data structure has better performance than a data structure that supports a general-purpose vector interface, both in space and time. (In a time test I run, bitset is almost five times faster than vector .) If the size of the bit set you want to manage is unknown, you need to use vector <bool>.
It seems that another case should use vector <bool> instead of bitset: when it is important to interact with STL generic algorithms. STL generic algorithms use selector, while vector <bool> provides selector (we can see V in the above example. begin () and V. end (), bitset does not. You can use array syntax to access a single bit in bitset, but it does not have the in () and end () member functions.
However, you should not let this lack stop you! Although bitset does not have the STL container interface, it is still a very good (fixed size) container. If you use bitset to make sense, and if you still need to select a child, you can define a simple "subscript Selection Sub" adapter to select a sub-expression (such as * I) convert to array representation (for example, B [N]).
Obviously: maintain a subscript and pointer to the container. Most of the details are used when implementing random iterator. See Listing 1. We also define some non-member auxiliary functions, begin () and end (). They accept a bitset as the parameter. (The iterator we display in Listing 1 is not as common as it may be: if we are willing to accept a slightly bulky interface, we can define a class that can work with any type similar to the array. A general-purpose subscript Selection Sub-adapter is often used to process the pre-STL container class, sometimes even when processing STL containers such as vector .)
Using bitset_iterator, bitset can now interact with STL components: for example, you can copy a bitset to vector <bool>:
STD: bitset <10> B;
...
STD: vector <bool>
B (begin (B), end (B ));
However, if you have carefully read Listing 1, you may have noticed a problem with bitset_iterator: The name is a lie, because bitset_iterator is not really an iterator. If I is an iterator, * I should return the reference of the object I refers. Bitset_iterator does not do this: const bitset_iterator returns bool instead of const bool &. bitset_iterator of the modifiable version returns a proxy object of the bitset type: reference instead of bool &.
Because bitwise is not addressable, this is the best we can do. In fact, vector <bool>: iterator also acts in the same way-once again, this means that vector <bool> is not a real STL container. Bitset_iterator and vecotr <bool>: iterator is not very correct, but both are close enough to iterator, so they can be used a lot (not all !) Where iterator is expected.
Summary
Arrays of Boolean values are common in large programs, and the C ++ standard Runtime Library provides several methods to represent such arrays. I don't have all columns. For example, you can use valarray <bool>. In some cases, it is suitable for representing a sparse bitvector, just like set <size_t>.
In many cases, the easiest way is to use STD: bitset. If you know the size of your Boolean array during compilation, or you can specify at least a reasonable upper limit, bitset is simpler and more efficient. Bitset interfaces are annoying, but they can be easily bypassed by some auxiliary functions.
Listing 1-bitset_iterator, an iterator adaptor class for STD: bitset
Template <bool flag, class iftrue, class iffalse>
Struct if;
Template <class iftrue, class iffalse>
Struct If <true, iftrue, iffalse> {
Typedef iftrue val;
};
Template <class iftrue, class iffalse>
Struct If <false, iftrue, iffalse> {
Typedef iffalse val;
};
Template <STD: size_t N, bool is_const>
Class bitset_iterator {
PRIVATE:
Typedef STD: bitset <n> bitset;
Typedef typename If <is_const, const bitset, bitset >:: Val
Qbitset;
Typedef STD: random_access_iterator_tag
Iterator_category;
Typedef bool value_type;
Typedef STD: ptrdiff_t difference_type;
Typedef typename If <is_const, const bool, bool >:: Val *
Pointer;
Typedef typename If <is_const,
Bool,
Typename bitset: Reference >:: Val
Reference;
Qbitset * B;
STD: size_t N;
Public:
Bitset_iterator (): B (), n (){}
Bitset_iterator (qbitset & B, STD: size_t sz)
: B (& B), n (sz ){}
Bitset_iterator (const bitset_iterator <n, false> & X)
: B (X. B), n (x. N ){}
Bitset_iterator & operator = (const bitset_iterator & X ){
B = x. B;
N = x. N;
}
Public:
Reference operator * () const {return (* B) [N];}
Reference operator [] (STD: ptrdiff_t X) const {
Return (* B) [n + x];
}
Bitset_iterator & operator ++ () {++ N; return * This ;}
Bitset_iterator operator ++ (INT ){
++ N;
Return bitset_iterator (* B, n-1 );
}
Bitset_iterator & operator -- () {-- N; return * This ;}
Bitset_iterator operator -- (INT ){
-- N;
Return bitset_iterator (* B, n + 1 );
}
Bitset_iterator operator + (STD: ptrdiff_t X) const {
Return bitset_iterator (* B, n + x );
}
Bitset_iterator & operator + = (STD: ptrdiff_t X ){
N + = X;
Return * this;
}
Bitset_iterator operator-(STD: ptrdiff_t X) const {
Return bitset_iterator (* B, n-x );
}
Bitset_iterator & operator-= (STD: ptrdiff_t X ){
N-= X;
Return * this;
}
Public:
Friend bool operator = (bitset_iterator X,
Bitset_iterator y ){
Return X. B = Y. B & X. n = Y. N;
}
Friend bool Operator! = (Bitset_iterator X,
Bitset_iterator y ){
Return! (X = y );
}
Friend bool operator <(bitset_iterator X,
Bitset_iterator y ){
Return X. n <Y. N;
}
Friend bool operator> (bitset_iterator X,
Bitset_iterator y ){
Return Y <X;
}
Friend bool operator <= (bitset_iterator X,
Bitset_iterator y ){
Return! (Y <X );
}
Friend bool operator> = (bitset_iterator X,
Bitset_iterator y ){
Return! (X <Y );
}
Friend STD: ptrdiff_t operator-(bitset_iterator X,
Bitset_iterator y ){
Return X. N-y. N;
}
Friend bitset_iterator operator + (STD: ptrdiff_t N1,
Bitset_iterator X ){
Return bitset_iterator (* X. B, X. N + N1 );
}
};
Template <STD: size_t n>
Bitset_iterator <n, true>
Begin (const STD: bitset <n> & B ){
Return bitset_iterator <n, true> (B, 0 );
}
Template <STD: size_t n>
Bitset_iterator <n, true>
End (const STD: bitset <n> & B ){
Return bitset_iterator <n, true> (B, n );
}
Template <STD: size_t n>
Bitset_iterator <n, false>
Begin (STD: bitset <n> & B ){
Return bitset_iterator <n, false> (B, 0 );
}
Template <STD: size_t n>
Bitset_iterator <n, false>
End (STD: bitset <n> & B ){
Return bitset_iterator <n, false> (B, n );
}
-End of listing-