B. Stroustrup - The C++ Programming Language (794319), страница 33
Текст из файла (страница 33)
However, like most containers, forward_list offers forward iterators that can be used to traverse the sequence by algorithms and for-statements (§33.1.1).The standard library provides a mechanism, iterator_traits that allows us to check which kind ofiterator is supported. Given that, we can improve the range sort() from §4.5.6 to accept either avector or a forward_list. For example:void test(vector<string>& v, forward_list<int>& lst){sort(v); // sor t the vectorsort(lst); // sor t the singly-linked list}The techniques needed to make that work are generally useful.First, I write two helper functions that take an extra argument indicating whether they are to beused for random-access iterators or forward iterators.
The version taking random-access iteratorarguments is trivial:Section 5.4.2.1iterator_traitstemplate<typename Ran>void sort_helper(Ran beg, Ran end, random_access_iterator_tag){sort(beg,end);// just sort it125// for random-access iterators// we can subscript into [beg:end)}The version for forward iterators is almost as simple; just copy the list into a vector, sort, and copyback again:template<typename For>// for forward iteratorsvoid sort_helper(For beg, For end, forward_iterator_tag)// we can traverse [beg:end){vector<decltype(∗beg)> v {beg,end};// initialize a vector from [beg:end)sort(v.begin(),v.end());copy(v.begin(),v.end(),beg);// copy the elements back}The decltype() is a built-in type function that returns the declared type of its argument (§6.3.6.3).Thus, v is a vector<X> where X is the element type of the input sequence.The real ‘‘type magic’’ is in the selection of helper functions:template<typname C>void sort(C& c){using Iter = Iterator_type<C>;sort_helper(c.begin(),c.end(),Iterator_category<Iter>{});}Here, I use two type functions: Iterator_type<C> returns the iterator type of C (that is, C::iterator) andthen Iterator_category<Iter>{} constructs a ‘‘tag’’ value indicating the kind of iterator provided:• std::random_access_iterator_tag if C’s iterator supports random access.• std::forward_iterator_tag if C’s iterator supports forward iteration.Given that, we can select between the two sorting algorithms at compile time.
This technique,called tag dispatch is one of several used in the standard library and elsewhere to improve flexibility and performance.The standard-library support for techniques for using iterators, such as tag dispatch, comes inthe form of a simple class template iterator_traits from <iterator> (§33.1.3). This allows simple definitions of the type functions used in sort():template<typename C>using Iterator_type = typename C::iterator;// C’s iterator typetemplate<typename Iter>using Iterator_category = typename std::iterator_traits<Iter>::iterator_category; // Iter’s categor yIf you don’t want to know what kind of ‘‘compile-time type magic’’ is used to provide the standardlibrary features, you are free to ignore facilities such as iterator_traits.
But then you can’t use thetechniques they support to improve your own code.126A Tour of C++: Concurrency and UtilitiesChapter 55.4.2.2 Type PredicatesA standard-library type predicate is a simple type function that answers a fundamental questionabout types. For example:bool b1 = Is_arithmetic<int>();// yes, int is an arithmetic typebool b2 = Is_arithmetic<string>(); // no, std::string is not an arithmetic typeThese predicates are found in <type_traits> and described in §35.4.1. Other examples are is_class,is_pod, is_literal_type, has_virtual_destructor, and is_base_of. They are most useful when we writetemplates.
For example:template<typename Scalar>class complex {Scalar re, im;public:static_assert(Is_arithmetic<Scalar>(), "Sorry, I only support complex of arithmetic types");// ...};To improve readability compared to using the standard library directly, I defined a type function:template<typename T>constexpr bool Is_arithmetic(){return std::is_arithmetic<T>::value ;}Older programs use ::value directly instead of (), but I consider that quite ugly and it exposes implementation details.5.4.3 pair and tupleOften, we need some data that is just data; that is, a collection of values, rather than an object of aclass with a well-defined semantics and an invariant for its value (§2.4.3.2, §13.4).
In such cases,we could define a simple struct with an appropriate set of appropriately named members. Alternatively, we could let the standard library write the definition for us. For example, the standardlibrary algorithm equal_range (§32.6.1) returns a pair of iterators specifying a sub-sequence meetinga predicate:template<typename Forward_iterator, typename T, typename Compare>pair<Forward_iterator,Forward_iterator>equal_range(Forward_iterator first, Forward_iterator last, const T& val, Compare cmp);Given a sorted sequence [first:last), equal_range() will return the pair representing the subsequencethat matches the predicate cmp.
We can use that to search in a sorted sequence of Records:auto rec_eq = [](const Record& r1, const Record& r2) { return r1.name<r2.name;};// compare namesvoid f(const vector<Record>& v)// assume that v is sorted on its "name" field{auto er = equal_range(v.begin(),v.end(),Record{"Reg"},rec_eq);Section 5.4.3pairfor (auto p = er.first; p!=er.second; ++p)cout << ∗p;and tuple127// print all equal records// assume that << is defined for Record}The first member of a pair is called first and the second member is called second.
This naming isnot particularly creative and may look a bit odd at first, but such consistent naming is a boon whenwe want to write generic code.The standard-library pair (from <utility>) is quite frequently used in the standard library andelsewhere. A pair provides operators, such as =, ==, and <, if its elements do. The make_pair() function makes it easy to create a pair without explicitly mentioning its type (§34.2.4.1). For example:void f(vector<string>& v){auto pp = make_pair(v.begin(),2);// ...}// pp is a pair<vector<string>::iterator,int>If you need more than two elements (or less), you can use tuple (from <utility>; §34.2.4.2).
A tupleis a heterogeneous sequence of elements; for example:tuple<string,int,double> t2("Sild",123, 3.14); // the type is explicitly specifiedauto t = make_tuple(string("Herring"),10, 1.23);// the type is deduced// t is a tuple<string,int,double>string s = get<0>(t); // get first element of tupleint x = get<1>(t);double d = get<2>(t);The elements of a tuple are numbered (starting with zero), rather than named the way elements ofpairs are (first and second).
To get compile-time selection of elements, I must unfortunately use theugly get<1>(t), rather than get(t,1) or t[1] (§28.5.2).Like pairs, tuples can be assigned and compared if their elements can be.A pair is common in interfaces because often we want to return more than one value, such as aresult and an indicator of the quality of that result. It is less common to need three or more parts toa result, so tuples are more often found in the implementations of generic algorithms.5.5 Regular ExpressionsRegular expressions are a powerful tool for text processing. They provide a way to simply andtersely describe patterns in text (e.g., a U.S.
ZIP code such as TX 77845, or an ISO-style date, suchas 2009−06−07) and to efficiently find such patterns in text. In <regex>, the standard library providessupport for regular expressions in the form of the std::regex class and its supporting functions. Togive a taste of the style of the regex library, let us define and print a pattern:regex pat (R"(\w{2}\s∗\d{5}(−\d{4})?)");cout << "pattern: " << pat << '\n';// ZIP code pattern: XXddddd-dddd and variants128A Tour of C++: Concurrency and UtilitiesChapter 5People who have used regular expressions in just about any language will find \w{2}\s∗\d{5}(−\d{4})?familiar.
It specifies a pattern starting with two letters \w{2} optionally followed by some space \s∗followed by five digits \d{5} and optionally followed by a dash and four digits −\d{4}. If you are notfamiliar with regular expressions, this may be a good time to learn about them ([Stroustrup,2009],[Maddock,2009], [Friedl,1997]). Regular expressions are summarized in §37.1.1.To express the pattern, I use a raw string literal (§7.3.2.1) starting with R"( and terminated by )".This allows backslashes and quotes to be used directly in the string.The simplest way of using a pattern is to search for it in a stream:int lineno = 0;for (string line; getline(cin,line);) {// read into line buffer++lineno;smatch matches;// matched strings go hereif (regex_search(line,matches,pat))// search for pat in linecout << lineno << ": " << matches[0] << '\n';}The regex_search(line,matches,pat) searches the line for anything that matches the regular expressionstored in pat and if it finds any matches, it stores them in matches.
If no match was found,regex_search(line,matches,pat) returns false. The matches variable is of type smatch. The ‘‘s’’stands for ‘‘sub’’ and an smatch is a vector of sub-matches. The first element, here matches[0], isthe complete match.For a more complete description see Chapter 37.5.6 MathC++ wasn’t designed primarily with numerical computation in mind. However, C++ is heavilyused for numerical computation and the standard library reflects that.5.6.1 Mathematical Functions and AlgorithmsIn <cmath>, we find the ‘‘usual mathematical functions,’’ such as sqrt(), log(), and sin() for arguments of type float, double, and long double (§40.3).
Complex number versions of these functionsare found in <complex> (§40.4).In <numeric>, we find a small set of generalized numerical algorithms, such as accumulate(). Forexample:void f(){list<double> lst {1, 2, 3, 4, 5, 9999.99999};auto s = accumulate(lst.begin(),lst.end(),0.0); // calculate the sumcout << s << '\n';// print 10014.9999}These algorithms work for every standard-library sequence and can have operations supplied asarguments (§40.6).Section 5.6.2Complex Numbers1295.6.2 Complex NumbersThe standard library supports a family of complex number types along the lines of the complexclass described in §2.3. To support complex numbers where the scalars are single-precision floating-point numbers (floats), double-precision floating-point numbers (doubles), etc., the standardlibrary complex is a template:template<typename Scalar>class complex {public:complex(const Scalar& re ={}, const Scalar& im ={});// ...};The usual arithmetic operations and the most common mathematical functions are supported forcomplex numbers.