目录

第17章 特殊的库设施

17.1 tuple类型

tuple是一个类似pair的模板。每一个pair类型有2个不同类型的成员。一个tuple同样有不同类型的成员,但是它可以有任意数量的成员。tuple类型定义在tuple头文件。

注解 tuple可以认为是一个“快捷且脏”的数据结构。

17.1.1 定义和初始化tuple

当我们定义一个tuple,需要指定每个成员的类型:

1
2
3
tuple<size_t, size_t, size_t> threeD; // all three members set to 0
tuple<string, vector<double>, int, list<int>>
    someVal("constants", {3.14, 2.718}, 42, {0,1,2,3,4,5});

tuple默认构造函数值初始化每一个成员。tuple构造函数是explicit的,因此必须使用直接初始化语法:

1
2
tuple<size_t, size_t, size_t> threeD =  {1,2,3};  // error
tuple<size_t, size_t, size_t> threeD{1,2,3};      // ok

类似于make_pair函数,标准库定义了一个make_tuple函数生成tuple对象:

1
2
// tuple that represents a bookstore transaction: ISBN, count, price per book
auto item = make_tuple("0-999-78345-X", 3, 20.00);

和make_pair一样,make_tuple函数使用其参数的类型推导tuple的类型。上面这个例子种,item类型为tupple<const char*, int, double>。

访问tuple成员

tuple的成员是未命名的,我们通过一个标准库函数模板get访问其成员。为了使用get我们必须指定一个显式模板实参,这个实参是我们想要访问的成员的位置。传递tuple对象给get,get返回指定成员的引用:

1
2
3
4
auto book = get<0>(item);      // returns the first member of item
auto cnt = get<1>(item);       // returns the second member of item
auto price = get<2>(item)/cnt; // returns the last member of item
get<2>(item) *= 0.8;           // apply 20% discount

尖括号<>中的值必须是整形常量表达式。

如果不知道tuple的类型,我们可以使用2个辅助的类模板找出tuple成员的数量和类型:

1
2
3
4
5
typedef decltype(item) trans; // trans is the type of item
// returns the number of members in object's of type trans
size_t sz = tuple_size<trans>::value;  // returns 3
// cnt has the same type as the second member in item
tuple_element<1, trans>::type cnt = get<1>(item); // cnt is an int

为了使用tuple_size或tuple_element,我们需要直到tuple对象的类型。通常最简单的确定对象类型的方法是使用decltype。

tuple_size有一个公开的静态数据成员value,表示指定tuple成员的数量。tuple_element有一个公开成员type表示指定成员的类型。

关系和相等操作符

只有当2个tuple有相同数量的成员时,才能进行比较。为使用==操作符,tuple之间的每一对成员必须可以使用==操作符。关系操作符同样的规则。

1
2
3
4
5
6
7
tuple<string, string> duo("1", "2");
tuple<size_t, size_t> twoD(1, 2);
bool b = (duo == twoD); // error: can't compare a size_t and a string
tuple<size_t, size_t, size_t> threeD(1, 2, 3);
b = (twoD < threeD);    // error: differing number of members
tuple<size_t, size_t> origin(0, 0);
b = (origin < twoD);    // ok: b is true

注解 因为tuple定义<和==操作符,我们可以将tuple的元素传递给标准库算法,也可以将tuple作为ordered容器的键。

17.1.2 使用tuple返回多个值

tuple的一个常见应用是从一个函数返回多个值。

返回tuple的函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// matches has three members: an index of a store and iterators into that store's vector
typedef tuple<vector<Sales_data>::size_type,
    vector<Sales_data>::const_iterator,
    vector<Sales_data>::const_iterator> matches;
// files holds the transactions for every store
// findBook returns a vector with an entry for each store that sold the given book
vector<matches>
findBook(const vector<vector<Sales_data>> &files, const string &book)
{
    vector<matches> ret; // initially empty
    // for each store find the range of matching books, if any
    for (auto it = files.cbegin(); it != files.cend(); ++it)
    {
        // find the range of Sales_data that have the same ISBN
        auto found = equal_range(it->cbegin(), it->cend(),
            book, compareIsbn);
        if (found.first != found.second) // this store had sales
            // remember the index of this store and the matching range
            ret.push_back(make_tuple(it - files.cbegin(),
                found.first,
                found.second));
    }
    return ret; // empty if no matches found
}

使用函数返回的tuple

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
void reportResults(istream &in, ostream &os,
    const vector<vector<Sales_data>> &files)
{
    string s;  // book to look for
    while (in >> s) {
        auto trans = findBook(files, s); // stores that sold this book
        if (trans.empty()) {
            cout << s << " not found in any stores" << endl;
            continue;  // get the next book to look for
        }
        for (const auto &store : trans)  // for every store with a sale
            // get<n> returns the specified member from the tuple in store
            os << "store " << get<0>(store) << " sales: "
                << accumulate(get<1>(store), get<2>(store), Sales_data(s))
                << endl;
    }
}

17.2 bitset类型

bitset类型定义在bitset头文件

17.2.1 定义和初始化bitset

bitset类是一个类模板,和array类一样,拥有固定长度。当定义bitset对象时,需要指定bitset将要包含的位数:

1
bitset<32> bitvec(1U); // 32 bits; low-order bit is 1, remaining bits are 0

bitset中的位通过位置访问。

用unsigned值初始化bitset

当使用一个整型值初始化一个bitset时,值被转化为unsigned long long的位模式。如果bitset大小比unsigned long long大,则剩下的高位置为0,否则超出bitset大小的高位被丢弃。

1
2
3
4
5
6
// bitvec1 is smaller than the initializer; high-order bits from the initializer are discarded
bitset<13> bitvec1 (0xbeef);  // bits are 1111011101111
// bitvec2 is larger than the initializer; high-order bits in bitvec2 are set to zero
bitset<20> bitvec2(0xbeef);  // bits are 00001011111011101111
// on machines with 64-bit long long 0ULL is 64 bits of 0, so ~0ULL  is 64 ones
bitset<128> bitvec3(~0ULL); // bits 0 ... 63 are one; 63 ... 127 are zero

用字符串初始化bitset

可以使用string或指向字符数组的指针初始化bitset。任一种情况,字符直接表示位模式。

1
bitset<32> bitvec4("1100"); // bits 2 and 3 are 1, all others are 0

如果字符串包含少于bitset大小的字符,则高位置为0。

注解 string和bitset的下标是相反关联的:string最右边的字符用来初始化bitset最低位。

可以使用子字符串初始化bitset:

1
2
3
string str("1111111000000011001101");
bitset<32> bitvec5(str, 5, 4); // four bits starting at str[5], 1100
bitset<32> bitvec6(str, str.size()-4); // use last four characters

bitset操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
bitset<32> bitvec(1U); // 32 bits; low-order bit is 1, remaining bits are 0
bool is_set = bitvec.any();      // true, one bit is set
bool is_not_set = bitvec.none(); // false, one bit is set
bool all_set = bitvec.all();     // false, only one bit is set
size_t onBits = bitvec.count();  // returns 1
size_t sz = bitvec.size();       // returns 32
bitvec.flip();     // reverses the value of all the bits in bitvec
bitvec.reset();    // sets all the bits to 0
bitvec.set();      // sets all the bits to 1
bitvec.flip(0);   // reverses the value of the first bit
bitvec.set(bitvec.size() - 1);  // turns on the last bit
bitvec.set(0, 0); // turns off the first bit
bitvec.reset(i);  // turns off the ith bit
bitvec.test(0);   // returns false because the first bit is off
bitvec[0] = 0;    // turn off the bit at position 0
bitvec[31] = bitvec[0]; // set the last bit to the same value as the first bit
bitvec[0].flip(); // flip the value of the bit at position 0
~bitvec[0]; // equivalent operation; flips the bit at position 0
bool b = bitvec[0]; // convert the value of bitvec[0] to bool

#### 获取bitset的值

to_ulong和to_ullong操作返回和bitset对象同样位模式的数值。只有在bitset大小小于或等于unsigned long或unsigned long long类型时才可用:

1
unsigned long ulong = bitvec3.to_ulong();

注解 如果bitset不能放进指定类型,这2个操作抛出overflow_error异常。

bitset IO操作

输入操作符从输入流读取字符到一个临时string对象,直到读满bitset大小或遇到一个不是0或1的字符,或者遇到输入操作。然后bitset由这个临时string初始化。输出操作符打印bitset对象的位模式。

1
2
3
bitset<16> bits;
cin >> bits;  // read up to 16 1 or 0 characters from cin
cout << "bits: " << bits << endl; // print what we just read

使用bitset

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
bool status;
// version using bitwise operators
unsigned long quizA = 0;      // this value is used as a collection of bits
quizA |= 1UL << 27;           // indicate student number 27 passed
status = quizA & (1UL << 27); // check how student number 27 did
quizA &= ~(1UL << 27);        // student number 27 failed
// equivalent actions using the bitset library
bitset<30> quizB;     // allocate one bit per student; all bits initialized to 0
quizB.set(27);        // indicate student number 27 passed
status = quizB[27];   // check how student number 27 did
quizB.reset(27);      // student number 27 failed

17.3 正则表达式

正则表达式是描述一串字符的一种方式。RE库定义在regex头文件,是新库的一部分。

说明
regex 正则表达式类
regex_match 根据正则表达式匹配字符串
regex_search 查找第一个匹配正则表达式的子串
regex_replace 使用指定的格式替换正则表达式
sregex_iterator 迭代器适配器,调用regex_search遍历字符串中匹配项
smatch 容器类,包含查找的结果集
ssub_match 匹配子表达式的结果集

regex类表示一个正则表达式。除了初始化和赋值,regex没有什么操作。函数regex_match和regex_search确定给定的字符串是否匹配指定的regex。如果整个字符串匹配正则表达式,regex_match返回true。如果有子串匹配,则regex_search返回true。还有一个regex_replace函数。

regex_match和regex_search的参数

(seq, m, r, mft) (seq, r, mft)

使用正则表达式库