Standard Template Library (STL) is a general purpose libraryconsisting of containers, generic algorithms, iterators, function objects,allocators, adapters and data structures. The data structures used by thealgorithms areabstract in the sense that the algorithms can be used with(practically) any data type.The algorithms can process these abstract data types because they aretemplate based. This chapter does not cover templateconstruction(see chapter21 for that). Rather, it focuses on theuseof the algorithms.
Several elements also used by the standard template library have already beendiscussed in theC++ Annotations. In chapter12 abstract containerswere discussed, and in section11.11 function objects were introduced.Also,iterators were mentioned at several places in this document.
The main components of the STL are covered in this and the nextchapter. Iterators, adapters, smart pointers, multi threading and otherfeatures of the STL are discussed in coming sections. Genericalgorithms are covered in the next chapter (19).
Allocators take care of the memory allocation within theSTL. The defaultallocator class suffices for most applications, and isnot further discussed in theC++ Annotations.
All elements of the STL are defined in thestandard namespace. Therefore, ausing namespace std or a comparabledirective is required unless it is preferred to specify the required namespaceexplicitly. In header files thestd namespace should explicitlybe used (cf. section7.11.1).
In this chapter the emptyangle bracket notation is frequently used. Incode a typename must be supplied between the angle brackets. E.g.,plus<>is used in theC++ Annotations, but in codeplus<string> may be encountered.
<functional> header file must be included.Function objects play important roles in genericalgorithms. For example, there exists a generic algorithmsortexpecting two iterators defining the range of objects that should be sorted,as well as a function object calling the appropriate comparison operator fortwo objects. Let's take a quick look at this situation. Assume strings arestored in a vector, and we want to sort the vector in descending order. Inthat case, sorting the vectorstringVec is as simple as:
sort(stringVec.begin(), stringVec.end(), greater<string>());
The last argument is recognized as aconstructor: it is aninstantiation of thegreater<> class template, applied tostrings. This object is called as a function object by thesortgeneric algorithm. The generic algorithm calls the function object'soperator() member to compare twostring objects. The function object'soperator() will, in turn, calloperator> of thestring datatype. Eventually, whensort returns, the first element of the vector willcontain the string having the greateststring value of all.
The function object'soperator() itself isnot visible at thispoint. Don't confuse the parentheses in the `greater<string>()' argumentwith callingoperator(). Whenoperator() is actually used insidesort, it receives two arguments: two strings to compare for`greaterness'. Sincegreater<string>::operator() is definedinline, thecall itself is not actually present in the abovesort call. Insteadsort callsstring::operator> throughgreater<string>::operator().
Now that we know that a constructor is passed as argument to (many) genericalgorithms, we can design our own function objects. Assume we want to sort ourvector case-insensitively. How do we proceed? First we note that the defaultstring::operator< (for an incremental sort) is not appropriate, as it doescase sensitive comparisons. So, we provide our ownCaseInsensitive class,which compares two strings case insensitively. Using thePOSIX functionstrcasecmp, the following program performs the trick. Itcase-insensitively sorts its command-line arguments in ascending alphabeticorder:
#include <iostream> #include <string> #include <cstring> #include <algorithm> using namespace std; class CaseInsensitive { public: bool operator()(string const &left, string const &right) const { return strcasecmp(left.c_str(), right.c_str()) < 0; } }; int main(int argc, char **argv) { sort(argv, argv + argc, CaseInsensitive{}); for (int idx = 0; idx < argc; ++idx) cout << argv[idx] << " "; cout << '\n'; } The default constructor of theclass CaseInsensitive is used toprovidesort with its final argument. So the only member function thatmust be defined isCaseInsensitive::operator(). Since we know it's calledwithstring arguments, we define it to expect twostring arguments,which are used when callingstrcasecmp. Furthermore, the function calloperatoroperator() is definedinline, so that it does not produceoverhead when called by thesort function. Thesort function calls thefunction object with various combinations ofstrings. If the compilergrants our inline requests, it will in fact callstrcasecmp, skipping twoextra function calls.The comparison function object is often apredefined function object.Predefined function object classes are available for many commonly usedoperations. In the following sections the available predefined functionobjects are presented, together with some examples showing their use. Near theend of the section about function objectsfunction adapters areintroduced.
Predefined function objects are used predominantly with genericalgorithms. Predefined function objects exists for arithmetic, relational, andlogical operations.
plus<Type> is available. If we replaceType bysize_t then the additionoperator forsize_t values is used, if we replaceType bystring,the addition operator for strings is used. For example: #include <iostream> #include <string> #include <functional> using namespace std; int main(int argc, char **argv) { plus<size_t> uAdd; // function object to add size_ts cout << "3 + 5 = " << uAdd(3, 5) << '\n'; plus<string> sAdd; // function object to add strings cout << "argv[0] + argv[1] = " << sAdd(argv[0], argv[1]) << '\n'; } /* Output when called as: a.out going 3 + 5 = 8 argv[0] + argv[1] = a.outgoing */ Why is this useful? Note that the function object can be used with allkinds of data types (not only with the predefined datatypes) supporting theoperator called by the function object.Suppose we want to perform an operation on a left hand side operand which isalways the same variable and a right hand side argument for which, in turn,all elements of an array should be used. E.g., we want to compute the sum ofall elements in an array; or we want to concatenate all the strings in atext-array. In situations like these function objects come in handy.
As stated, function objects are heavily used in the context of the genericalgorithms, so let's take a quick look ahead at yet another one.
The generic algorithmaccumulate visits all elements specified by aniterator-range, and performs a requested binary operation on a common elementand each of the elements in the range, returning the accumulated result aftervisiting all elements specified by the iterator range. It's easy to use thisalgorithm. The next program accumulates all command line arguments and printsthe final string:
#include <iostream> #include <string> #include <functional> #include <numeric> using namespace std; int main(int argc, char **argv) { string result = accumulate(argv, argv + argc, string{}, plus<string>()); cout << "All concatenated arguments: " << result << '\n'; } The first two arguments define the (iterator) range of elements to visit,the third argument isstring. This anonymous string object provides aninitial value. We could also have used"All concatenated arguments: "s
in which case thecout statement could simply have beencout << result << '\n'. The string-addition operation is used, called fromplus<string>. Thefinal concatenated string is returned.
Now we define a classTime, overloadingoperator+. Again, we canapply the predefined function objectplus, now tailored to our newlydefined datatype, to add times:
#include <iostream> #include <string> #include <vector> #include <functional> #include <numeric> using namespace std; class Time { friend ostream &operator<<(ostream &str, Time const &time); size_t d_days; size_t d_hours; size_t d_minutes; size_t d_seconds; public: Time(size_t hours, size_t minutes, size_t seconds); Time &operator+=(Time const &rhs); }; Time operator+(Time const &lhs, Time const &rhs) { Time ret(lhs); return ret += rhs; } Time::Time(size_t hours, size_t minutes, size_t seconds) : d_days(0), d_hours(hours), d_minutes(minutes), d_seconds(seconds) {} Time &Time::operator+=(Time const &rhs) { d_seconds += rhs.d_seconds; d_minutes += rhs.d_minutes + d_seconds / 60; d_hours += rhs.d_hours + d_minutes / 60; d_days += rhs.d_days + d_hours / 24; d_seconds %= 60; d_minutes %= 60; d_hours %= 24; return *this; } ostream &operator<<(ostream &str, Time const &time) { return str << time.d_days << " days, " << time.d_hours << " hours, " << time.d_minutes << " minutes and " << time.d_seconds << " seconds."; } int main(int argc, char **argv) { vector<Time> tvector; tvector.push_back(Time( 1, 10, 20)); tvector.push_back(Time(10, 30, 40)); tvector.push_back(Time(20, 50, 0)); tvector.push_back(Time(30, 20, 30)); cout << accumulate ( tvector.begin(), tvector.end(), Time(0, 0, 0), plus<Time>() ) << '\n'; } // Displays: 2 days, 14 hours, 51 minutes and 30 seconds. The design of the above program is fairly straightforward.Timedefines a constructor, it defines an insertion operator and it defines its ownoperator+, adding two time objects. Inmain fourTime objects arestored in avector<Time> object. Then,accumulate is used to computethe accumulated time. It returns aTime object, which is inserted intocout.While this section's first example illustrated using anamed functionobject, the last two examples illustrate howanonymous objects can bepassed to the (accumulate) function.
The STL supports the following set of arithmetic function objects. Thefunction call operator (operator()) of these function objects calls thematching arithmetic operator for the objects that are passed to the functioncall operator, returning that arithmetic operator's return value. Thearithmetic operator that is actually called is mentioned below:
plus<>: calls the binaryoperator+;minus<>: calls the binaryoperator-;multiplies<>: calls the binaryoperator*;divides<>: callsoperator/;modulus<>: callsoperator%;negate<>: calls the unaryoperator-. This arithmeticfunction object is a unary function object as it expects one argument.transform generic algorithm is used to togglethe signs of all elements of an array.Transformexpects two iterators, defining the range of objects to be transformed; aniterator defining the begin of the destination range (which may be the sameiterator as the first argument); and a function object defining a unaryoperation for the indicated data type. #include <iostream> #include <string> #include <functional> #include <algorithm> using namespace std; int main(int argc, char **argv) { int iArr[] = { 1, -2, 3, -4, 5, -6 }; transform(iArr, iArr + 6, iArr, negate<int>()); for (int idx = 0; idx < 6; ++idx) cout << iArr[idx] << ", "; cout << '\n'; } // Displays: -1, 2, -3, 4, -5, 6,==, !=,>, >=, < and<=.The STL supports the following set of relational function objects. Thefunction call operator (operator()) of these function objects calls thematching relational operator for the objects that are passed to the functioncall operator, returning that relational operator's return value. Therelational operator that is actually called is mentioned below:
equal_to<>: callsoperator==;not_equal_to<>: callsoperator!=;greater<>: callsoperator>;greater_equal<>: callsoperator>=;less<>: this object's memberoperator() callsoperator<;less_equal<>: callsoperator<=.sort is: #include <iostream> #include <string> #include <functional> #include <algorithm> using namespace std; int main(int argc, char **argv) { sort(argv, argv + argc, greater_equal<string>()); for (int idx = 0; idx < argc; ++idx) cout << argv[idx] << " "; cout << '\n'; sort(argv, argv + argc, less<string>()); for (int idx = 0; idx < argc; ++idx) cout << argv[idx] << " "; cout << '\n'; } The example illustrates how strings may be sorted alphabetically andreversed alphabetically. By passinggreater_equal<string> the strings aresorted indecreasing order (the first word will be the 'greatest'), bypassingless<string> the strings are sorted inincreasing order (thefirst word will be the 'smallest').Note thatargv containschar * values, and that the relationalfunction object expects astring. The promotion fromchar const * tostring is silently performed.
and, or, andnot.The STL supports the following set of logical function objects. Thefunction call operator (operator()) of these function objects calls thematching logical operator for the objects that are passed to the functioncall operator, returning that logical operator's return value. Thelogical operator that is actually called is mentioned below:
operator! is provided in the following trivialprogram, usingtransform to transformthe logical values stored in an array: #include <iostream> #include <string> #include <functional> #include <algorithm> using namespace std; int main(int argc, char **argv) { bool bArr[] = {true, true, true, false, false, false}; size_t const bArrSize = sizeof(bArr) / sizeof(bool); for (size_t idx = 0; idx < bArrSize; ++idx) cout << bArr[idx] << " "; cout << '\n'; transform(bArr, bArr + bArrSize, bArr, logical_not<bool>()); for (size_t idx = 0; idx < bArrSize; ++idx) cout << bArr[idx] << " "; cout << '\n'; } /* Displays: 1 1 1 0 0 0 0 0 0 1 1 1 */truethe negator returnsfalse and vv.The standard negator isstd::not_fn, declared in the<functional> header file.
The functionnot_fn expects a (movable) object as its argument, returningthe negated value of the return value of its argument's function calloperator.
As an example consider amain function defining an array ofintvalues:
int main() { int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; }To count the number of even valuescount_if, using a lambda function canbe used:
cout << count_if(arr, arr + size(arr), [&](int value) { return (value & 1) == 0; } ) << '\n';To count the number of odd values,not_fn can be used in the above codelike so:
cout << count_if(arr, arr + size(arr), not_fn( [&](int value) { return (value & 1) == 0; } ) ) << '\n';Of course, in this simple example the lambda function could also easily havebeen modified. But if instead of a lambda function an existing classimplementing a function object had been used it would have been difficult orimpossible to change the behavior of that class. If the class offers movingoperations thennot_fn can be used to negate the values returned by thatclass's function call operator.
<iterator> header filemust be included.The standard iterator (std::iterator) isnowdeprecated (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0174r1.html#2.1),and the compiler issues a corresponding warning. Consequently,std::iterator should no longer be used when designing your own iterators(section22.14 describes how to design your own).
Iterators are objects acting like pointers. Iterators have the followinggeneral characteristics:
== and!= operators. Theordering operators (e.g.,>,<)can usually not be used.iter,*iter represents the object theiterator points to (alternatively,iter-> can be used to reach the membersof the object the iterator points to).iter,iter.base() returns theaddress of*iter. It returns the same type as&*iter. E.g., vector<int> vi{ 1, 2, 3 }; int *ip = vi.begin().base(); cout << *ip << '\n'; // shows: 1++iter oriter++ advances the iterator to the nextelement. The notion of advancing an iterator to the next element isconsequently applied: several containers supportreverse_iterator types,in which the++iter operation actually reaches a previous element in asequence.vector anddeque. For such containersiter + 2 points to the second elementbeyond the one to whichiter points. See also section18.2.1,coveringstd::distance.#include <vector>#include <iostream>using namespace std;int main(){ vector<int>::iterator vi; cout << &*vi; // prints 0}iterator). These members are commonly calledbegin andend and (for reversed iterators (typereverse_iterator))rbegin andrend. Whereas reverse iterators can be constructed from ordinary (forward)iterators usingreverse_iterator constructors as in:
string str; auto revit = string::reverse_iterator{ str.begin() };the opposite is not accomplished that way. To retrieve the forwarditerator corresponding to a reverse iterator, thereverse_iterator.base()member can be used. E.g., to obtain the forward iterator corresponding torevit use
auto forward { revit.base() };Standard practice requires iterator ranges to beleft inclusive. The notation[left, right) indicates thatleftis an iterator pointing to the first element, whileright is an iteratorpointing justbeyond the last element. The iterator range isemptywhenleft == right.
The following example shows how all elements of a vector of strings can beinserted intocout using its iterator ranges[begin(), end()), and[rbegin(), rend()). Note that thefor-loops for both ranges areidentical. Furthermore it nicely illustrates how theauto keyword can beused to define the type of the loop control variable instead of using a muchmore verbose variable definition likevector<string>::iterator (see alsosection3.3.7):
#include <iostream> #include <vector> #include <string> using namespace std; int main(int argc, char **argv) { vector<string> args(argv, argv + argc); for (auto iter = args.begin(); iter != args.end(); ++iter) cout << *iter << " "; cout << '\n'; for (auto iter = args.rbegin(); iter != args.rend(); ++iter) cout << *iter << " "; cout << '\n'; }Furthermore, the STL definesconst_iterator types that must be usedwhen visiting a series of elements in a constant container. Whereas theelements of the vector in the previous example could have been altered, theelements of the vector in the next example are immutable, andconst_iterators are required:
#include <iostream> #include <vector> #include <string> using namespace std; int main(int argc, char **argv) { vector<string> const args(argv, argv + argc); for ( vector<string>::const_iterator iter = args.begin(); iter != args.end(); ++iter ) cout << *iter << " "; cout << '\n'; for ( vector<string>::const_reverse_iterator iter = args.rbegin(); iter != args.rend(); ++iter ) cout << *iter << " "; cout << '\n'; } The examples also illustrate that plain pointers can be used as iterators. Theinitializationvector<string> args(argv, argv + argc) provides theargs vector with a pair of pointer-based iterators:argv points to thefirst element to initializeargs with,argv + argc points just beyondthe last element to be used,++argv reaches the next command lineargument. This is a general pointer characteristic, which is why they too canbe used in situations whereiterators are expected.The STL defines six types of iterators. Theseiterator types are expected by generic algorithms, and in order to create aparticular type of iterator yourself it is important to know theircharacteristics. In general, iterators (see alsosection22.14) must define:
operator==, testing two iterators for equality,operator!=, testing two iterators for inequality,operator++, incrementing the iterator, as prefix operator,operator*, to access the element the iterator refers to,InputIterators are used to read from a container. Thedereference operator is guaranteed to work asrvalueinexpressions. Instead of an InputIterator it is also possible to use (seebelow) Forward-, Bidirectional- or RandomAccessIterators. Notations likeInputIterator1andInputIterator2may be used as well. In these cases,numbers are used to indicate which iterators `belong together'. E.g., thegeneric algorithminner_producthas the followingprototype:Type inner_product(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, Type init);
InputIterator1 first1andInputIterator1 last1define a pair ofinput iterators on one range, whileInputIterator2 first2defines thebeginning of another range. Analogous notations may be used with otheriterator types.
OutputIterators can be used to write to a container. Thedereference operator is guaranteed to work as anlvaluein expressions,but not necessarily asrvalue. Instead of an OutputIterator it is alsopossible to use (see below) Forward-, Bidirectional- orRandomAccessIterators.
ForwardIterators combine InputIterators andOutputIterators. They can be used to traverse containers in one direction,for reading and/or writing. Instead of a ForwardIterator it is also possibleto use (see below) Bidirectional- or RandomAccessIterators.
BidirectionalIterators can be used to traverse containers inboth directions, for reading and writing. Instead of a BidirectionalIteratorit is also possible to use (see below) a RandomAccessIterator.
RandomAccessIterators providerandom access to containerelements. An algorithm likesort requires aRandomAccessIterator, and can thereforenot be used to sort the elementsof lists or maps, which only provide BidirectionalIterators.ContiguousIterators are like random access iterators, but inaddition guarantee that the elements these iterators point to are storedcontiguously in memory. A container likestd::vector offers contiguousiterators.Using pointer arithmetic to compute the number of elements between twoiterators in, e.g., astd::list orstd::unordered_map is not possible,as these containers do not store their elements consecutively in memory.
The functionstd::distance fills in that little gap:std::distance expects two InputIterators and returns the number ofelements between them.
Before usingdistance the<iterator> header file must be included.
If the iterator specified as first argument exceeds the iterator specified asits second argument then the number of elements is non-positive, otherwise itis non-negative. If the number of elements cannot be determined (e.g., theiterators do not refer to elements in the same container), thendistance'sreturn value is undefined.
Example:
#include <iostream> #include <unordered_map> using namespace std; int main() { unordered_map<int, int> myMap = {{1, 2}, {3, 5}, {-8, 12}}; cout << distance(++myMap.begin(), myMap.end()) << '\n'; // shows: 2 }Theiterator header file also defines the functionstd::size,returning the number of elements in a containers (as returned by thecontainer'ssize member) or of an array whose dimension is known to thecompiler at the point ofstd::size's call. E.g., if the size of an arraydata is known to the compiler, then to call a functionhandler(expecting the address of the first element of an array and the address of thelocation just beyond that array) the following statement can be used:
handler(data, data + std::size(data));
As noted, thestd::size function is defined in theiteratorheader. However, it's also guaranteed available when including the header fileof a container supporting iterators (including thestring header file).
copy genericalgorithm has three parameters. The first two define the range of visitedelements, the third defines the first position where the resultsof the copy operation should be stored.With thecopy algorithm the number of elements to copy is usuallyavailable beforehand, since that number can usually be provided by pointerarithmetic. However, situations exist where pointer arithmetic cannot beused. Analogously, the number of resulting elements sometimes differs from thenumber of elements in the initial range. The generic algorithmunique_copy is a case in point. Here the number ofelements that are copied to the destination container is normally not knownbeforehand.
In situations like these aninserter adapterfunction can often be used to create elements in the destination container.There are three types of inserter adapters:
back_inserter: calls the container'spush_back member to addnew elements at the end of the container. E.g., to copy all elements ofsource in reversed order to the back ofdestination, using thecopy generic algorithm:copy(source.rbegin(), source.rend(), back_inserter(destination));
front_inserter calls the container'spush_front member, addingnew elements at the beginning of the container. E.g., to copy all elements ofsource to the front of the destination container (thereby also reversingthe order of the elements):copy(source.begin(), source.end(), front_inserter(destination));
inserter calls the container'sinsert member adding new elementsstarting at a specified starting point. E.g., to copy all elements ofsource to the destination container, starting at the beginning ofdestination, shifting up existing elements to beyond the newly insertedelements:copy(source.begin(), source.end(), inserter(destination, destination.begin()));
using value_type = Data, whereData is the data type stored inthe class offeringpush_back, push_front orinsert members (Example:using value_type = std::string);using const_reference = const &value_typeback_inserter, this iterator expects the name of acontainer supporting a memberpush_back. The inserter'soperator()member calls the container'spush_back member. Objects of any class supporting apush_backmember can be passed as arguments toback_inserter provided the class addsusing const_reference = DataType const &;
to its interface (whereDataType const & is the type of the parameterof the class's memberpush_back). Example:
#include <iostream> #include <algorithm> #include <iterator> using namespace std; class Insertable { public: using value_type = int; using const_reference = int const &; void push_back(int const &) {} }; int main() { int arr[] = {1}; Insertable insertable; copy(arr, arr + 1, back_inserter(insertable)); }istream_iterator<Type> can be used to define a setof iterators foristream objects. The general form of theistream_iterator iterator is:istream_iterator<Type> identifier(istream &in)
Here,Type is the type of the data elements read from theistreamstream. It is used as the `begin' iterator in an iterator range.Type maybe any type for whichoperator>> is defined in combination withistreamobjects.
The default constructor is used as the end-iterator and corresponds to theend-of-stream. For example,
istream_iterator<string> endOfStream;
Thestream object that was specified when defining thebegin-iterator isnot mentioned with the default constructor.
Usingback_inserter andistream_iterator adapters, all stringsfrom a stream can easily be stored in a container. Example (usinganonymousistream_iterator adapters):
#include <iostream> #include <iterator> #include <string> #include <vector> #include <algorithm> using namespace std; int main() { vector<string> vs; copy(istream_iterator<string>(cin), istream_iterator<string>(), back_inserter(vs)); for ( vector<string>::const_iterator begin = vs.begin(), end = vs.end(); begin != end; ++begin ) cout << *begin << ' '; cout << '\n'; }streambuf objects.To read fromstreambuf objects supporting input operationsistreambuf_iterators can be used, supporting theoperations that are also available foristream_iterator. Different fromthe latter iterator typeistreambuf_iterators support three constructors:
istreambuf_iterator<Type>:istreambuf_iterator constructor. It represents theend-of-streamcondition when extracting values of typeType from thestreambuf.istreambuf_iterator<Type>(streambuf *):streambuf may be used when defining anistreambuf_iterator. It represents the begin iterator of an iteratorrange.istreambuf_iterator<Type>(istream):istreambuf_iterator. It accesses theistream'sstreambuf and italso represents the begin iterator of an iterator range.istreambuf_iterators andostreambuf_iterators.ostream_iterator<Type> adapter can be used to passanostream to algorithms expecting an OutputIterator. Two constructors areavailable for definingostream_iterators:ostream_iterator<Type> identifier(ostream &outStream); ostream_iterator<Type> identifier(ostream &outStream, char const *delim);
Type is the type of the data elements that should be inserted into anostream. It may be any type for whichoperator<< is defined incombination withostream objects. The latter constructor can be used toseparate the individualType data elements bydelimiter strings. Theformer constructor does not use any delimiters.
The example shows howistream_iterators and anostream_iterator may be used to copy information of a file to anotherfile. A subtlety here is that you probably want to usein.unsetf(ios::skipws). It is used to clear theios::skipws flag. As a consequence whitespace characters aresimply returned by the operator, and the file is copied character bycharacter. Here is the program:
#include <iostream> #include <algorithm> #include <iterator> using namespace std; int main() { cin.unsetf(ios::skipws); copy(istream_iterator<char>(cin), istream_iterator<char>(), ostream_iterator<char>(cout)); }streambuf objects.To write tostreambuf objects supporting output operationsostreambuf_iterators can be used, supporting theoperations that are also available forostream_iterator.Ostreambuf_iterators support two constructors:
ostreambuf_iterator<Type>(streambuf *):streambuf may be used when defining anostreambuf_iterator. It can be used as an OutputIterator.ostreambuf_iterator<Type>(ostream):ostreambuf_iterator. It accesses theostream'sstreambuf and itcan also be used as an OutputIterator.istreambuf_iterators andostreambuf_iteratorswhen copying a stream in yet another way. Since the stream'sstreambufsare directly accessed the streams and stream flags are bypassed. Consequentlythere is no need to clearios::skipws as in the previous section, whilethe next program's efficiency probably also exceeds the efficiency of theprogram shown in the previous section. #include <iostream> #include <algorithm> #include <iterator> using namespace std; int main() { istreambuf_iterator<char> in(cin.rdbuf()); istreambuf_iterator<char> eof; ostreambuf_iterator<char> out(cout.rdbuf()); copy(in, eof, out); }std::move on the source container's elements it's also possible to usemake_move_iterator(std::make_move_iterator(iterator iter)). The function'sargument is an iterator referring to a movable element, and the range ofelements to move from one container to another can be specified by callingtwomake_move_iterators. One receives the source's begin iterator, andother one the source's end iterator. The example illustrates how wordscan be moved into astd::vector<std::string>: #include <iostream> #include <algorithm> #include <vector> #include <string> #include <iterator> using namespace std; size_t fstNonEmpty(vector<string> const &vs) { return find_if(vs.begin(), vs.end(), [&](string const &str) { return str != ""; } ) - vs.begin(); } int main() { vector<string> vs; copy(istream_iterator<string>(cin), istream_iterator<string>(), back_inserter(vs)); cout << "vs contains " << vs.size() << " words\n" "first non-empty word at index " << fstNonEmpty(vs) << "\n" "moving the first half into vector v2\n"; vector<string> v2{ make_move_iterator(vs.begin()), make_move_iterator(vs.begin() + vs.size() / 2) }; cout << "vs contains " << vs.size() << " words\n" "first non-empty word at index " << fstNonEmpty(vs) << "\n" "v2 contains " << v2.size() << " words\n"; } Note that the elements in the source container have merely moved into thedestination container: the elements in the source container are empty aftermoving. When moving other types of objects the results may be different,although in all cases the source objects should keep their valid states.unique_ptr class presented in this section the<memory> header file must be included.When pointers are used to access dynamically allocated memory strictbookkeeping is required to prevent memory leaks. When a pointer variablereferring to dynamically allocated memory goes out of scope, the dynamicallyallocated memory becomes inaccessible and the program suffers from amemory leak. Consequently, the programmer has to make sure that the dynamicallyallocated memory is returned to the common pool just before the pointervariable goes out of scope.
When a pointer variable points to a dynamically allocated single value orobject, bookkeeping requirements are greatly simplified when the pointervariable is defined as astd::unique_ptr object.
Unique_ptrs areobjects masquerading as pointers. Since they are objects,their destructors are called when they go out of scope. Their destructorsautomatically delete the dynamically allocated memory to which theypoint. Unique_ptrs (and their cousins shared_ptrs (cf. section18.4)are also calledsmart pointers).
Unique_ptrs have several special characteristics:
unique_ptr to anothermove semantics isused. If move semantics is not available compilation fails. On the otherhand, if compilation succeeds then the used containers or generic algorithmssupport the use ofunique_ptrs. Here is an example:std::unique_ptr<int> up1(new int);std::unique_ptr<int> up2(up1); // compilation error
The second definition fails to compile asunique_ptr's copyconstructor is private (the same holds true for the assignment operator). Buttheunique_ptr classdoes offer facilities to initialize and assignfromrvalue references:
class unique_ptr // interface partially shown{ public: unique_ptr(unique_ptr &&tmp); // rvalues bind here private: unique_ptr(const unique_ptr &other);};In the next example move semantics is used and so it compiles correctly:
unique_ptr<int> cp(unique_ptr<int>(new int));
unique_ptr object should only point to memory that was madeavailable dynamically, as only dynamically allocated memory can be deleted.unique_ptr objects should not be allowed to point to thesame block of dynamically allocated memory. Theunique_ptr's interface wasdesigned to prevent this from happening. Once aunique_ptr object goes outof scope, it deletes the memory it points to, immediately changing any otherobject also pointing to the allocated memory into a wildpointer.Derived is derived fromBase, then a newlyallocatedDerived class object can be assigned to aunique_ptr<Base>,without having to define a virtual destructor forBase. TheBase *pointer that is returned by theunique_ptr object can simply be caststatically toDerived, andDerived's destructor is automaticallycalled as well, if theunique_ptr definition is provided with adeleter function address. This is illustrated in the next example: class Base { ... }; class Derived: public Base { ... public: // assume Derived has a member void process() static void deleter(Base *bp); }; void Derived::deleter(Base *bp) { delete static_cast<Derived *>(bp); } int main() { unique_ptr<Base, void (*)(Base *)> bp(new Derived, &Derived::deleter); static_cast<Derived *>(bp.get())->process(); // OK! } // here ~Derived is called: no polymorphism required.unique_ptr offers several member functions to access thepointer itself or to have aunique_ptr point to another block ofmemory. These member functions (andunique_ptr constructors) areintroduced in the next few sections.Unique_ptr can also be used with containers and (generic) algorithms. Theycan properly destruct any type of object, as their constructors acceptcustomizable deleters. In addition, arrays can be handled byunique_ptrs.
unique_ptrobjects. Each definition contains the usual<type> specifier betweenangle brackets:unique_ptr object thatdoes not point to a particular block of memory. Its pointer is initialized to0 (zero):unique_ptr<type> identifier;
This form is discussed in section18.3.2.
unique_ptr object.Following the use of the move constructor itsunique_ptr argument nolonger points to the dynamically allocated memory and its pointer data memberis turned into a zero-pointer:unique_ptr<type> identifier(another unique_ptr for type);
This form is discussed in section18.3.3.
unique_ptr objectto the block of dynamically allocated memory that is passed to the object'sconstructor. Optionallydeleter can be provided. A (free) function (orfunction object) receiving theunique_ptr's pointer as its argument can bepassed as deleter. It is supposed to return the dynamically allocatedmemory to the common pool (doing nothing if the pointer equals zero).unique_ptr<type> identifier (new-expression [, deleter]);
This form is discussed in section18.3.4.
Unique_ptr's default constructor defines aunique_ptr not pointing to a particular block of memory:unique_ptr<type> identifier;
The pointer controlled by theunique_ptrobject is initialized to0 (zero). Although theunique_ptr objectitself is not the pointer, its valuecan be compared to0. Example:
unique_ptr<int> ip; if (!ip) cout << "0-pointer with a unique_ptr object\n";
Alternatively, the memberget can be used (cf. section18.3.5).
unique_ptr may be initializedusing an rvalue reference to aunique_ptr object for the same type:unique_ptr<type> identifier(other unique_ptr object);
The move constructor is used, e.g., in the following example:
void mover(unique_ptr<string> &¶m) { unique_ptr<string> tmp(move(param)); }Analogously, the assignment operator can beused. Aunique_ptr object may be assigned to a temporaryunique_ptrobject of the same type (again move-semantics is used). For example:
#include <iostream> #include <memory> #include <string> using namespace std; int main() { unique_ptr<string> hello1{ new string{ "Hello world" } }; unique_ptr<string> hello2{ move(hello1) }; unique_ptr<string> hello3; hello3 = move(hello2); cout << // *hello1 << '\n' << // would have segfaulted // *hello2 << '\n' << // same *hello3 << '\n'; } // Displays: Hello world The example illustrates thathello1 is initialized by a pointer to a dynamically allocatedstring (see the next section).unique_ptr hello2 grabs the pointer controlled byhello1using a move constructor. This effectively changeshello1 into a0-pointer.hello3 is defined as a defaultunique_ptr<string>. Butthen it grabs its value using move-assignment fromhello2 (which, as aconsequence, is changed into a 0-pointer).hello1 orhello2 had been inserted intocout asegmentation fault would have resulted. The reason for this should now beclear: it is caused by dereferencing 0-pointers. In the end, onlyhello3actually points to the originally allocatedstring.unique_ptr is most often initializedusing a pointer to dynamically allocated memory. The generic form is: unique_ptr<type [, deleter_type]> identifier(new-expression [, deleter = deleter_type{}]);The second (template) argument (deleter(_type)) is optional and may refer to a free function, a function object handling thedestruction of the allocated memory, or a lambda function. A deleter is used,e.g., in situations where a double pointer is allocated and the destructionmust visit each nested pointer to destroy the allocated memory (see below foran illustration).
Here is an example initializing aunique_ptr pointing to astringobject:
unique_ptr<string> strPtr{ new string{ "Hello world" } };The argument that is passed to the constructor is the pointer returned byoperator new. Note thattype doesnot mention the pointer. The type that is used in theunique_ptr constructionis the same as the type that is used innew expressions.
Here is an example showing how an explicitly defined deleter may be used todelete a dynamically allocated array of pointers to strings:
#include <string> #include <memory> using namespace std; struct Deleter { size_t d_size; Deleter(size_t size = 0) : d_size(size) {} void operator()(string **ptr) const { for (size_t idx = 0; idx < d_size; ++idx) delete ptr[idx]; delete[] ptr; } }; int main() { unique_ptr<string *, Deleter> sp2{ new string *[10](), Deleter{ 10 } }; // for illustration purposes: // retrieving the Deleter [[maybe_unused]] Deleter &obj = sp2.get_deleter(); }Aunique_ptr can be used to reach the member functions that are available forobjects allocated by thenew expression. These members can be reached asif theunique_ptr was a plain pointer to the dynamically allocatedobject. For example, in the following program the text `C++' is insertedbehind the word `hello':
#include <iostream> #include <memory> #include <cstring> using namespace std; int main() { unique_ptr<string> sp{ new string{ "Hello world" } }; cout << *sp << '\n'; sp->insert(strlen("Hello "), "C++ "); cout << *sp << '\n'; } /* Displays: Hello world Hello C++ world */unique_ptr offers the following operators:unique_ptr<Type> &operator=(unique_ptr<Type> &&tmp):unique_ptr object to thelvalueunique_ptr object usingmove semantics. So, the rvalue objectloses the memory it pointed at and turns into a 0-pointer. An existingunique_ptr may be assigned to anotherunique_ptr by converting it to an rvalue reference first usingstd::move. Example:unique_ptr<int> ip1(new int);unique_ptr<int> ip2;ip2 = std::move(ip1);
operator bool() const:false if theunique_ptr does not point to memory (i.e., itsget member, see below, returns 0). Otherwise,true is returned.Type &operator*():unique_ptr object . It acts like a normal pointer dereference operator.Type *operator->():unique_ptr object. This operator allows you to select members of an object accessible via aunique_ptr object. Example:unique_ptr<string> sp{ new string{ "hello" } };cout << sp->c_str();The classunique_ptr supports the followingmember functions:
Type *get():unique_ptr object is returned. It acts likeoperator->. The returned pointer can be inspected. If it is zero theunique_ptr object does not point to any memory.Deleter &unique_ptr<Type>::get_deleter():unique_ptr is returned.Type *release():unique_ptr object is returned. At the same time the object itself becomes a 0-pointer (i.e., its pointer data member is turned into a 0-pointer). This member can be used to transfer the information accessible via aunique_ptr object to a plainType pointer. After calling this member the proper destruction of the dynamically allocated memory is theresponsibility of the programmer.void reset(Type *):unique_ptr object is returned to the common pool; the object thereupon controls the memory to which the argument that is passed to the function points. It can also be called without argument, turning the object into a 0-pointer. This member function can be used to assign a new block of dynamically allocated memory to aunique_ptr object.void swap(unique_ptr<Type> &):unique_ptrs are swapped.unique_ptr is used to store arrays the dereferencing operator makeslittle sense but with arraysunique_ptr objects benefit from indexoperators. The distinction between a single objectunique_ptr and aunique_ptr referring to a dynamically allocated array of objects isrealized through a template specialization.With dynamically allocated arrays the following syntax is available:
[]) notation is used to specify that the smart pointer controls a dynamically allocatedarray. Example:unique_ptr<int[]> intArr(new int[3]);
intArr[2] = intArr[0];
delete[] rather thandelete.unique_ptr the classstd::shared_ptr<Type> is available, which is a referencecounting smart pointer.Before usingshared_ptrs the<memory> header file must be included.
The shared pointer automatically destroys its content once its referencecount has decayed to zero. As withunique_ptr, when defining ashared_ptr<Base> to store a newly allocatedDerived class object, thereturnedBase * may be cast to aDerived * using astatic_cast:polymorphism isn't required, and when resetting theshared_ptr or when theshared_ptr goes out of scope, no slicing occurs, andDerived'sdestructor (or, if configured: deleter) is called (cf. section18.3).
Shared_ptrs support copy and move constructors as well as standard andmove overloaded assignment operators.
Likeunique_ptrs, shared_ptrs may refer to dynamically allocated arrays.
shared_ptrobjects. Each definition contains the usual<type> specifier betweenangle brackets:shared_ptr object thatdoes not point to a particular block of memory. Its pointer is initialized to0 (zero):shared_ptr<type> identifier;
This form is discussed in section18.4.2.
shared_ptr so that bothobjects share the memory pointed at by the existing object. The copyconstructor also increments theshared_ptr's reference count. Example:shared_ptr<string> org{ new string{ "hi there" } };shared_ptr<string> copy(org); // reference count now 2shared_ptr with the pointerand reference count of a temporaryshared_ptr. The temporaryshared_ptr is changed into a 0-pointer. An existingshared_ptr mayhave its data moved to a newly definedshared_ptr (turning the existingshared_ptr into a 0-pointer as well). In the next example a temporary,anonymousshared_ptr object is constructed, which is then used toconstructgrabber. Sincegrabber's constructor receives an anonymoustemporary object, the compiler usesshared_ptr's move constructor:shared_ptr<string> grabber{ shared_ptr<string>{ new string{ "hi there" } } };shared_ptr objectto the block of dynamically allocated memory that is passed to the object'sconstructor. Optionallydeleter can be provided. A (free) function (orfunction object) receiving theshared_ptr's pointer as its argument can bepassed as deleter. It is supposed to return the dynamically allocatedmemory to the common pool (doing nothing if the pointer equals zero).shared_ptr<type> identifier (new-expression [, deleter]);
This form is discussed in section18.4.3.
Shared_ptr's default constructor defines ashared_ptr not pointing to a particular block of memory:shared_ptr<type> identifier;
The pointer controlled by theshared_ptrobject is initialized to0 (zero). Although theshared_ptr objectitself is not the pointer, its valuecan be compared to0. Example:
shared_ptr<int> ip; if (!ip) cout << "0-pointer with a shared_ptr object\n";
Alternatively, the memberget can be used (cf. section18.4.4).
shared_ptr is initialized by adynamically allocated block of memory. The generic form is:shared_ptr<type> identifier(new-expression [, deleter]);
The second argument (deleter) is optional and refers to a function object or free function handling thedestruction of the allocated memory. A deleter is used, e.g., in situationswhere a double pointer is allocated and the destruction must visit each nestedpointer to destroy the allocated memory (see below for an illustration). Itis used in situations comparable to those encountered withunique_ptr(cf. section18.3.4).
Here is an example initializing ashared_ptr pointing to astringobject:
shared_ptr<string> strPtr{ new string{ "Hello world" } };The argument that is passed to the constructor is the pointer returned byoperator new. Note thattype doesnot mention the pointer. The type that is used in theshared_ptr constructionis the same as the type that is used innew expressions.
The next example illustrates that twoshared_ptrs indeed share theirinformation. After modifying the information controlled by one of theobjects the information controlled by the other object is modified as well:
#include <iostream> #include <memory> #include <cstring> using namespace std; int main() { shared_ptr<string> sp(new string{ "Hello world" }); shared_ptr<string> sp2(sp); sp->insert(strlen("Hello "), "C++ "); cout << *sp << '\n' << *sp2 << '\n'; } /* Displays: Hello C++ world Hello C++ world */shared_ptr offers the followingoperators:shared_ptr &operator=(shared_ptr<Type> const &other):shared_ptr &operator=(shared_ptr<Type> &&tmp):operator bool() const:shared_ptr actually points to memorytrue is returned, otherwise,false is returned.Type &operator*():shared_ptr object is returned. It acts like a normal pointer.Type *operator->():shared_ptr object is returned. Example:shared_ptr<string> sp{ new string{ "hello" } };cout << sp->c_str() << '\n';The followingmember function member functions are supported:
Type *get():shared_ptr object is returned. It acts likeoperator->. The returned pointer can be inspected. If it is zero theshared_ptr object does not point to any memory.Deleter &get_deleter():shared_ptr's deleter (function or function object) is returned.void reset(Type *):shared_ptr object is reduced and if it decays to zero the memory it points to is deleted. Thereafter the object's information will refer to the argument that is passed to the function, setting its shared count to 1. It can also be called without argument, turning the object into a 0-pointer. This member function can be used to assign a new block of dynamically allocated memory to ashared_ptr object.void reset(Type *, DeleterType &&):Deleter type: ifType is a base-class and derived class objects are used, these derived class objects may require specific actions at destruction time. When the previous member is used, then eventually the newly assigned object's destructor is called without using an explicit deleter function. The current member ensures that by the time the shared counter has decayed to zero the provided deleter is used.void shared_ptr<Type>::swap(shared_ptr<Type> &&):shared_ptrs are swapped.bool unique() const:true is returned otherwise (including the situation where the object is a 0-pointer)false is returned.size_t use_count() const:shared_ptr objects. Consider the following two classes: struct Base {}; struct Derived: public Base {};As withunique_ptr, when defining ashared_ptr<Base> to store anewly allocatedDerived class object, the returnedBase * may be castto aDerived * using astatic_cast: polymorphism isn't required, andwhen resetting theshared_ptr or when theshared_ptr goes out ofscope, no slicing occurs, andDerived's destructor is called (cf. section18.3).
Of course, ashared_ptr<Derived> can easily be defined. Since aDerived object is also aBase object, a pointer toDerived canbe considered a pointer toBase without using casts, but astatic_castcould be used to force the interpretation of aDerived * to aBase *:
Derived d; static_cast<Base *>(&d);
However, a plainstatic_cast cannot be used when initializing a sharedpointer to aBase using theget member of a shared pointer to aDerived object. The following code snipped eventually results in anattempt to delete the dynamically allocatedBase object twice:
shared_ptr<Derived> sd{ new Derived }; shared_ptr<Base> sb{ static_cast<Base *>(sd.get()) };Sincesd andsb point at the same object~Base will be calledfor the same object whensb goes out of scope and whensd goes out ofscope, resulting in premature termination of the program due to adouble free error.
These errors can be prevented using casts that were specifically designedfor being used withshared_ptrs. These casts use specialized constructorsthat create ashared_ptr pointing to memory but shares ownership (i.e.,a reference count) with an existingshared_ptr. These special casts are:
std::static_pointer_cast<Base>(std::shared_ptr<Derived> ptr):shared_ptr to aBase class object is returned. The returnedshared_ptr refers to the base class portion of theDerived class towhich theshared_ptr<Derived> ptr refers. Example:shared_ptr<Derived> dp{ new Derived };shared_ptr<Base> bp = static_pointer_cast<Base>(dp);std::const_pointer_cast<Class>(std::shared_ptr<Class const> ptr):shared_ptr to aClass class object is returned. Thereturnedshared_ptr refers to a non-constClass object whereas theptr argument refers to aClass const object. Example:shared_ptr<Derived const> cp{ new Derived };shared_ptr<Derived> ncp = const_pointer_cast<Derived>(cp);std::dynamic_pointer_cast<Derived>(std::shared_ptr<Base> ptr):shared_ptr to aDerived class object is returned. TheBase class must have at least one virtual member function, and the classDerived, inheriting fromBase may have overriddenBase's virtualmember(s). The returnedshared_ptr refers to aDerived class object ifthe dynamic cast fromBase * toDerived * succeeded. If the dynamiccast did not succeed theshared_ptr'sget member returns 0. Example(assumeDerived andDerived2 were derived fromBase):shared_ptr<Base> bp(new Derived());cout << dynamic_pointer_cast<Derived>(bp).get() << ' ' << dynamic_pointer_cast<Derived2>(bp).get() << '\n';
The firstget returns a non-0 pointer value, the secondgetreturns 0.
shared_ptr class can also be used to handle dynamically allocatedarrays of objects. To use it on arrays simply use the square brackets whenspecifying theshared_ptr's type. Here is an example: struct Type { ~Type() { cout << "destr\n"; // show the object's destruction } }; int main() { shared_ptr<Type[]> sp{ new Type[3] }; sp[0] = sp[1]; }When theshared_ptr itself is initialized with an array of pointers, thenadeleter must be used to delete the memory the pointers point at. In thatcase the deleter is responsible for returning the memory to the common pool,usingdelete[] when eventually deleting the array of pointers:
struct Type { int value = 0; ~Type() { cout << "destr " << this << '\n'; } }; template<typename Tp> struct Deleter { size_t d_size; Deleter(size_t size) : d_size(size) {} void operator()(Tp **ptr) { for (; d_size--; ) delete ptr[d_size]; delete[] ptr; // <-- NOTE ! } }; int main() { shared_ptr<Type *[]> sp{ new Type *[2] {new Type, new Type}, Deleter<Type>{ 2 } }; }shared_ptr is initialized at definition timewith a pointer to a newly allocated object. Here is an example: std::shared_ptr<string> sptr{ new std::string{ "hello world" } }In such statementstwo memory allocation calls are used: one for theallocation of thestd::string and one used interally bystd::shared_ptr's constructor itself.
The two allocations can be combined into one single allocation (which isalso slightly more efficient than explicitly callingshared_ptr'sconstructor) using themake_shared template. The function templatestd::make_shared has the following prototype:
template<typename Type, typename ...Args> std::shared_ptr<Type> std::make_shared(Args ...args);
Before usingmake_shared the<memory> header file must be included.
This function template allocates an object of typeType, passingargs to its constructor (usingperfect forwarding, see section22.5.2), and returns ashared_ptr initialized with the address ofthe newly allocatedType object.
Here is how the abovesptr object can be initializedusingstd::make_shared. Notice the use ofauto which frees us fromhaving to specifysptr's type explicitly:
auto sptr(std::make_shared<std::string>("hello world"));After this initializationstd::shared_ptr<std::string> sptr has beendefined and initialized. It could be used as follows:
std::cout << *sptr << '\n';
In addition tomake_shared the functionstd::make_unique can be used. It can be usedmake_shared but returns astd::unique_ptr rather than ashared_ptr.
class Filter { istream *d_in; ostream *d_out; public: Filter(char const *in, char const *out); };Assume thatFilter objects filter information read from*d_in andwrite the filtered information to*d_out. Using pointers to streamsallows us to have them point at any kind of stream likeistreams,ifstreams, fstreams oristringstreams. The shown constructor could beimplemented like this:
Filter::Filter(char const *in, char const *out) : d_in(new ifstream{ in }), d_out(new ofstream{ out }) { if (!*d_in || !*d_out) throw "Input and/or output stream not available"s; }Of course, the construction could fail.new could throw an exception;the stream constructors could throw exceptions; or the streams could not beopened in which case an exception is thrown from the constructor's body. Usinga function try block helps. Note that ifd_in's initialization throws,there's nothing to be worried about. TheFilter object hasn't beenconstructed, its destructor is not called and processing continues at thepoint where the thrown exception is caught. ButFilter's destructor isalso not called whend_out's initialization or the constructor'sifstatement throws: no object, and hence no destructor is called. This mayresult in memory leaks, asdelete isn't called ford_in and/ord_out. To prevent this,d_in andd_out must first be initializedto 0 and only then the initialization can be performed:
Filter::Filter(char const *in, char const *out) try : d_in(0), d_out(0) { d_in = new ifstream{ in }; d_out = new ofstream{ out }; if (!*d_in || !*d_out) throw "Input and/or output stream not available"s; } catch (...) { delete d_out; delete d_in; }This quickly gets complicated, though. IfFilter harbors yet anotherdata member of a class whose constructor needs two streams then that datacannot be constructed or it must itself be converted into a pointer:
Filter::Filter(char const *in, char const *out) try : d_in(0), d_out(0) d_filterImp(*d_in, *d_out) // won't work { ... } // instead: Filter::Filter(char const *in, char const *out) try : d_in(0), d_out(0), d_filterImp(0) { d_in = new ifstream(in); d_out = new ofstream(out); d_filterImp = new FilterImp(*d_in, *d_out); ... } catch (...) { delete d_filterImp; delete d_out; delete d_in; }Although the latter alternative works, it quickly gets hairy. Insituations like these smart pointers should be used to prevent thehairiness. By defining the stream pointers as (smart pointer) objects theywill, once constructed, properly be destroyed even if the rest of theconstructor's code throws exceptions. Using aFilterImp and twounique_ptr data membersFilter's setup and its constructor becomes:
class Filter { std::unique_ptr<std::ifstream> d_in; std::unique_ptr<std::ofstream> d_out; FilterImp d_filterImp; ... }; Filter::Filter(char const *in, char const *out) try : d_in(new ifstream(in)), d_out(new ofstream(out)), d_filterImp(*d_in, *d_out) { if (!*d_in || !*d_out) throw "Input and/or output stream not available"s; }We're back at the original implementation but this time without having toworry about wild pointers and memory leaks. If one of the member initializersthrows the destructors of previously constructed data members (which are nowobjects) are always called.
As arule of thumb: when classes need to define pointer data membersthey should define those pointer data members as smart pointers if there's anychance that their constructors throw exceptions.
<=>, cf. section11.7.2) severalcomparison category classes were added to the standard namespace.Comparison classes are required when implementing the spaceship operator, andto use them (or when declaring and implementing the spachip operator) the<compare> header file must be included.
The weak class types do not supportsubstitutability. Substitutabilitymeans that if two objectsone andtwo are equal (so:one == two istrue), thenfun(one) == fun(two) is alsotrue. Herefun is anyfunction that only uses public const members of its argument that returnvalue types (as compared to pointer types) which also support comparisons(also calledcomparison-salient state).
The operators of comparison classes expect at least one argument of their ownclass types. The other argument may either also be of their own class types orit can be the value 0 (or any value that can be considered 0, likenullptr_t).
There are five comparison classes, primarily used when implementingspaceship operators:
weak_equality, used for classes that only support the== and!= operators, but not substitutability;strong_equality, used for classes that only support the== and!= operators, as well as substitutability;partial_ordering used for classes that support all comparison operators, do not support substitutability, and can be used when comparing incomparable arguments (i.e., all comparison operators return false);weak_ordering used for classes that support all comparison operators and that do not support substitutability;strong_ordering used for classes that support all comparison operators as well as substitutability;std::weak_equality is used when implementingthe spaceship operator for classes that only support (in)equality comparisons,but not substitutability. The class provides free functionsoperator== andoperator!= expectingweak_equality arguments (oneargument may be 0) and it defines two static objects:weak_equality::equivalent, indicating equality;weak_equality::nonequivalent, indicating non-equality;Note: at the current release of the GnuC++ compiler (10.0.0) this classis not yet available in the<compare> header file.
std::strong_equality is used when implementingthe spaceship operator for classes that only support (in)equality comparisonsas well as substitutability. The class provides free functionsoperator== andoperator!= expectingstrong_equality arguments (oneargument may be 0) and it defines four static objects:strong_equality::equal, indicating equality;strong_equality::equivalent, indicating equality;strong_equality::nonequal, indicating non-equality;strong_equality::nonequivalent, indicating non-equality;Note: at the current release of the GnuC++ compiler (10.0.0) this classis not yet available in the<compare> header file.
std::partial_ordering is used whenimplementing the spaceship operator for classes that support all comparisonoperators (where one operand may be zero), that do not supportsubstitutability, and whose objects, using the spaceship operator itself, canalso be compared to any other type of object.The classpartial_ordering provides free functions for all comparisonoperations (==, !=, <, <=, >, and>=) expectingpartial_orderingarguments (one argument may be 0). It also defines four static objects whichcan be returned by the spaceship operator:
partial_ordering::less, returned when the lhs operand of the spaceship operator should be ordered before the rhs operand;partial_ordering::equivalent, indicating equality: there is no ordering preference between the two operands of the spaceship operator;partial_ordering::greater, returned when the lhs operand of the spaceship operator should be ordered after the rhs operand;partial_ordering::unordered, returned when all comparison operators of a class implementing the spaceship operator should returnfalse (so==, !=, <, <=, >, and>= all returnfalse).As an example, consider road taxes. Trucks, cars, and motor cycles have to payroad taxes, but there's no road tax for bicycles. For ordering road taxes aclassRoadTax may be used, defining the following spaceship operator(assuming that all types of vehicles are derived from a classVehicle, andthatVehicle has a (virtual) memberdouble roadTax() returning theamount of road tax that is due for the various types of vehicles; the amountis negative if no road tax is required):
partial_ordering RoadTax::operator<=>(Vehicle const &lhs, Vehicle const &rhs) { return lhs.roadTax() < 0 or rhs.roadTax() < 0 ? partial_ordering::unordered : lhs.roadTax() < rhs.roadTax() ? partial_ordering::less : lhs.roadTax() > rhs.roadTax() ? partial_ordering::greater : partial_ordering::equivalent; }std::weak_ordering is used whenimplementing the spaceship operator for classes that support all comparisonoperators (where one operand may be zero), and that do not supportsubstitutability.The classweak_ordering differs from thepartial_ordering class inthatunordered cannot be used as a comparison result. Like the classpartial_ordering it provides free functions for all comparisonoperations (==, !=, <, <=, >, and>=) expectingpartial_orderingarguments (one argument may be 0). It also defines three static objects whichcan be returned by the spaceship operator:
weak_ordering::less, returned when the lhs operand of the spaceship operator should be ordered before the rhs operand;weak_ordering::equal, indicating equality: there is no ordering preference between the two operands of the spaceship operator;weak_ordering::greater, returned when the lhs operand of the spaceship operator should be ordered after the rhs operand;The example in the previous section can easily adapted to theweak_ordering comparison class: if theroadTax members of vehicles forwhich no road tax is due return zero thenRoadTax's spaceship operator canbe implemented this way:
weak_ordering RoadTax::operator<=>(Vehicle const &lhs, Vehicle const &rhs) { return lhs.roadTax() < rhs.roadTax() ? weak_ordering::less : lhs.roadTax() > rhs.roadTax() ? weak_ordering::greater : weak_ordering::equal; }std::strong_ordering is used whenimplementing the spaceship operator for classes that support all comparisonoperators (where one operand may be zero), as well as substitutability.The classstrong_ordering provides free functions for all comparisonoperations (==, !=, <, <=, >, and>=) expectingpartial_orderingarguments (one argument may be 0). It also defines three static objects whichcan be returned by the spaceship operator:
strong_ordering::less, returned when the lhs operand of the spaceship operator should be ordered before the rhs operand;strong_ordering::equal, indicating equality: there is no ordering preference between the two operands of the spaceship operator;strong_ordering::greater, returned when the lhs operand of the spaceship operator should be ordered after the rhs operand;An example where the classstrong_ordering was used has already beenprovided in section11.7.2, where the spaceship operator itself wasintroduced.
regcomp andregexec), but thededicated regular expression facilities have a richer interface than thetraditionalC facilities, and can be used in code using templates.Before using the specificC++ implementations of regular expressions theheader file<regex> must be included.
Regular expressions are extensively documented elsewhere (e.g.,regex(7),Friedl, J.E.FMastering Regular Expressions, O'Reilly). The reader is referred to these sources for a refresher on the topic ofregular expressions. In essence, regular expressions define a smallmeta-language recognizing textual units (like `numbers', `identifiers', etc.).They are extensively used in the context oflexical scanners (cf. section26.6.1) when defining the sequence of input characters associated withtokens. But they are also intensively used in other situations. Programslikesed(1) andgrep(1) use regular expressions to find pieces of textin files having certain characteristics, and a program likeperl(1) addssome `sugar' to the regular expression language, simplifying the constructionof regular expressions. However, though extremely useful, it is also wellknown that regular expressions tend to be very hard to read. Some even callthe regular expression language awrite-only language: while specifying aregular expression it's often clear why it's written in a particular way. Butthe opposite, understanding what a regular expression is supposed to representif you lack the proper context, can be extremely difficult. That's why, fromthe onset and as arule of thumb, it is stressed that an appropriatecomment should be provided, witheach regular expression, as to what it issupposed to match.
In the upcoming sections first a short overview of the regular expressionlanguage is provided, which is then followed by the facilitiesC++ iscurrently offering for using regular expressions. These facilities mainlyconsist of classes helping you to specify regular expression, matching them totext, and determining which parts of the text (if any) match (parts of) thetext being analyzed.
regex classes.C++'s default definition of regular expressions distinguishes thefollowingatoms:
x: the character `x';.: any character except for the newline character;[xyz]: a character class; in this case, either an `x', a `y', or a `z' matches the regular expression. See also the paragraph about character classes below;[abj-oZ]: a character class containing a range of characters; this regular expression matches an `a', a `b', any letter from `j' through `o', or a `Z'. See also the paragraph about character classes below;[^A-Z]: a negated character class: this regular expression matches any character but those in the class beyond^. In this case, any characterexcept for an uppercase letter. See also the paragraph about character classes below;[:predef:]: apredefined set of characters. See below for an overview. When used, it is interpreted as an element in a character class. It is therefore always embedded in a set of square brackets defining the character class (e.g.,[[:alnum:]]);\X: if X is `a', `b', `f', `n', `r', `t', or `v', then the ANSI-C interpretation of `\x'. Otherwise, a literal `X' (used to escape operators such as*);(r): the regular expressionr. It is used to override precedence (see below), but also to definer as amarked sub-expression whose matching characters may directly be retrieved from, e.g., astd::smatch object (cf. section18.8.3);(?:r): the regular expressionr. It is used to override precedence (see below), but it isnot regarded as amarked sub-expression;In addition to these basic atoms, the following special atoms are available(which can also be used in character classes):
\s: a whitespace character;\S: any character but a whitespace character;\d: a decimal digit character;\D: any character but a decimal digit character;\w: an alphanumeric character or an underscore (_) character;\W: any character but an alphanumeric character or an underscore (_) character.Atoms may be concatenated. Ifr ands are atoms then the regularexpressionrs matches a target text if the target text matchesrands, in that order (without any intermediate characters inside the target text). E.g., the regular expression[ab][cd] matches thetarget textac, but not the target texta:c.
Atoms may be combined using operators. Operators bind to the precedingatom. If an operator should operate on multiple atoms the atoms must besurrounded by parentheses (as in(r) used above: ifr isone, thenuse(one) when applying the operator to the wordone, instead of justthe finale). To use an operator character as an atom it can beescaped. Eg.,* represents an operator,\* the atom characterstar. Note that character classes do not recognize escape sequences:[\*]represents a character class consisting of two characters: a backslash and astar.
The following operators are supported (r ands represent regularexpression atoms):
r*: zero or morers;r+: one or morers;r?: zero or oners (that is, an optional r);r{m, n}: where1 <= m <= n: matches `r' at least m, but at most n times;r{m,}: where1 <= m: matches `r' at least m times;r{m}: where1 <= m: matches `r' exactly m times;r|s: matches either an `r' or an `s'. This operator has a lower priority than any of the multiplication operators;^r :^ is a pseudo operator. This expression matches `r', if appearing at the beginning of the target text. If the^-character is not the first character of a regular expression it is interpreted as a literal^-character;r$:$ is a pseudo operator. This expression matches `r', if appearing at the end of the target text. If the$-character is not the last character of a regular expression it is interpreted as a literal$-character;When a regular expression contains marked sub-expressions and multipliers, andthe marked sub-expressions are multiply matched, then the target's finalsub-string matching the marked sub-expression is reported as the text matchingthe marked sub-expression. E.g, when usingregex_search (cf. section18.8.4.3), marked sub-expression (((a|b)+\s?)), and target texta ab, thena a b is the fully matched text, whileb is reported as thesub-string matching the first and second marked sub-expressions.
\s, \S, \d, \D, \w, and\W; thecharacter range operator-; the end of character class operator];and, at the beginning of the character class,^. Except in combinationwith the special atoms the escape character is interpreted as a literalbackslash character (to define a character class containing a backslash and ad simply use[d\]).To add a closing bracket to a character class use[] immediately followingthe initial open-bracket, or start with[^] for a negated character classnot containing the closing bracket. Minus characters are used to definecharacter ranges (e.g.,[a-d], defining[abcd]) (be advised that theactual range may depend on the locale being used). To add a literal minuscharacter to a character class put it at the very beginning ([-, or[^-) or at the very end (-]) of a character class.
Once a character class has started, all subsequent characters are added to theclass's set of characters, until the final closing bracket (]) has beenreached.
In addition to characters and ranges of characters, character classes may alsocontainpredefined sets of character. They are:
[:alnum:] [:alpha:] [:blank:] [:cntrl:] [:digit:] [:graph:] [:lower:] [:print:] [:punct:] [:space:] [:upper:] [:xdigit:]
These predefined sets designate sets of characters equivalent to thecorresponding standardCisXXX function. For example,[:alnum:]defines all characters for whichisalnum(3) returns true.
(w)regex class presented in this section the<regex> header file must be included.The typesstd::regex andstd::wregex define regularexpression patterns. They define, respectively the typesbasic_regex<char> andbasic_regex<wchar_t>types. Below, the classregex is used, but in the exampleswregexcould also have been used.
Regular expression facilities were, to a large extent, implemented throughtemplates, using, e.g., thebasic_string<char> type (which is equal tostd::string). Likewise, generic types likeOutputIter (outputiterator) andBidirConstIter (bidirectional const iterator) are used withseveral functions. Such functions are function templates. Function templatesdetermine the actual types from the arguments that are provided atcall-time.
These are the steps that are commonly taken when using regular expressions:
regex object.The wayregex objects handle regular expressions can be configured using abit_or combined set ofstd::regex_constants values,defining aregex::flag_type value. Theseregex_constants are:
std::regex_constants::awk:/-characters, like/\w+/; for further details and for details of other regular expression grammars the reader should consult the man-pages of the respective programs);std::regex_constants::basic:std::regex_constants::collate:-) used in character classes defines a locale sensitive range (e.g.,[a-k]);std::regex_constants::ECMAScript:flag_type is used by default byregex constructors. The regular expression uses the ModifiedECMAScript regular expression grammar;std::regex_constants::egrep:regex_constants::extended, with the addition of the newline character ('\n') as an alternative for the'|'-operator;std::regex_constants::extended:std::regex_constants::grep:regex_constants::basic, with the addition of the newline character ('\n') as an alternative for the'|'-operator;std::regex_constants::icase:A matchesa andA;std::regex_constants::nosubs:(expr)) are treated as non-marked (?:expr);std::regex_constants::optimize:Constructors
The default, move and copy constructors are available. Actually, thedefault constructor defines one parameter of typeregex::flag_type, forwhich the valueregex_constants::ECMAScript is used by default.
regex():regex object not containing a regular expression;explicit regex(char const *pattern):regex object containing the regular expression found atpattern;regex(char const *pattern, std::size_t count):regex object containing the regular expression found at the firstcount characters ofpattern;explicit regex(std::string const &pattern):regex object containing the regular expression found atpattern. This constructor is defined as a member template, accepting abasic_string-type argument which may also use non-standard character traits and allocators;regex(ForwardIterator first, ForwardIterator last):regex object containing the regular expression found at the (forward) iterator range[first, last). This constructor is defined as a member template, accepting any forward iterator type (e.g., plainchar pointers) which can be used to define the regular expression's pattern;regex(std::initializer_list<Char> init):regex object containing the regular expression from the characters in the initializer listinit.Here are some examples:
std::regex re("\\w+"); // matches a sequence of alpha-numeric // and/or underscore characters std::regex re{'\\', 'w', '+'} ; // idem std::regex re(R"(\w+xxx")", 3); // idemMember functions
regex &operator=(RHS):char const *);std::string const & (or any compatiblestd::basic_string);std::initializer_list<char>;regex &assign(RHS):regex's constructors, including the (optional)regex_constants values;regex::flag_type flag() const:regex_constants flags that are active for the currentregex object. E.g., int main() { regex re; regex::flag_type flags = re.flags(); cout << // displays: 16 0 0 (re.flags() & regex_constants::ECMAScript) << ' ' << (re.flags() & regex_constants::icase) << ' ' << (re.flags() & regex_constants::awk) << ' ' << '\n'; }Note that when a combination offlag_type values is specified at construction-time that only those flags that were specified are set. E.g., whenre(regex_constants::icase) would have been specified thecout statement would have shown0 1 0. It's also possible to specify conflicting combinations of flag-values likeregex_constants::awk | regex_constants::grep. The construction of suchregex objects succeeds, but should be avoided.
locale_type get_loc() const:regex object;locale_type imbue(locale_type locale):regex object's current locale setting withlocale, returning the replaced locale;unsigned mark_count() const:regex object is returned. E.g., int main() { regex re("(\\w+)([[:alpha:]]+)"); cout << re.mark_count() << '\n'; // displays: 2 }void swap(regex &other) noexcept:regex object withother. Also available as a free function:void swap(regex &lhs, regex &rhs), swappinglhs andrhs.regex object is available, it can be used to match some target textagainst the regular expression. To match a target text against a regularexpression the following functions, described in the next section(18.8.4), are available:regex_match merely matches a target text against a regularexpression, informing the caller whether a match was found or not;regex_search also matches a target text against a regularexpression, but allows retrieval of matches of marked sub-expressions (i.e.,parenthesized regular expressions);regex_replace matches a target text against a regularexpression, and replaces pieces of matched sections of the target text byanother text.These functions must be provided with a target text and aregex object(which is not modified by these functions). Usually another argument, astd::match_results object is also passed to thesefunctions, to contain the results of the regular expression matchingprocedure.
Before using thematch_results class the<regex> header file must beincluded.
Examples of usingmatch_results objects are provided in section18.8.4. This and the next section are primarily for referentialpurposes.
Various specializations of the classmatch_results exist. Thespecialization that is used should match the specializations of the usedregex class. E.g., if the regular expression was specified as acharconst * thematch_results specialization should also operate oncharconst * values. The various specializations ofmatch_results have beengiven names that can easily be remembered, so selecting the appropriatespecialization is simple.
The classmatch_results has the following specializations:
cmatch:match_results<char const *>, using achar const * type of iterator. It should be used with aregex(char const *) regular expression specification;wcmatch:match_results<wchar_ const *>, using awchar_t const * type of iterator. It should be used with aregex(wchar_t const *) regular expression specification;smatch:match_results<std::string::const_iterator>, using astd::string::const_iterator type of iterator. It should be used with aregex(std::string const &) regular expression specification;wsmatch:match_results<std::wstring::const_iterator>, using astd::wstring::const_iterator type of iterator. It should be used with aregex(wstring const &) regular expression specification.Constructors
The default, copy, and move constructors are available. The defaultconstructor defines anAllocator const & parameter, which by default isinitialized to the default allocator. Normally, objects of the classmatch_results receive their match-related information by passing them tothe above-mentioned functions, likeregex_match. When returning from thesefunctions members of the classmatch_results can be used to retrievespecific results of the matching process.
Member functions
match_results &operator=:std::string const &operator[](size_t idx) const:idx. Withidx value 0 a reference to the full match is returned. Ifidx >= size() (see below) a reference to an empty sub-range of the target string is returned. The behavior of this member is undefined if the memberready() (see below) returnsfalse;Iterator begin() const:Iterator is a const-iterator forconst match_results objects;Iterator cbegin() const:Iterator is a const-iterator;Iterator cend() const:Iterator is a const-iterator;Iterator end() const:Iterator is a const-iterator forconst match_results objects;ReturnType format(Parameters) const:regex_replace function, and it is therefore covered in detail in that function's section (18.8.4.5);allocator_type get_allocator() const:bool empty() const:true if thematch_results object contains no matches (which is also returned after merely using the default constructor). Otherwise it returnsfalse;int length(size_t idx = 0) const:idx. By default the length of the full match is returned. Ifidx >= size() (see below) 0 is returned;size_type max_size() const:match_results object. This is an implementation dependent constant value;int position(size_t idx = 0) const:idx. By default the position of the first character of the full match is returned. Ifidx >= size() (see below) -1 is returned;std::string const &prefix() const:bool ready() const:match_results object. It receives its match results from one of the mentioned matching functions. Returnstrue once match results are available, andfalse otherwise.size_type size() const:(abc)|(def) and targetdefcon three submatches are reported: the total match (def); the empty text for(abc); anddef for the(def) marked sub-expression.Note: when multipliers are used only the last match is counted and reported. E.g., for the pattern(a|b)+ and targetaaabtwo sub-matches are reported: the total matchaaab, and the last match (b);
std::string str(size_t idx = 0) const:idx. By default this is the full match. Ifidx >= size() (see below) an empty string returned;std::string const &suffix() const:void swap(match_results &other) noexcept:match_results object withother. Also available as a free function:void swap(match_results &lhs, match_results &rhs), swappinglhs andrhs.<regex> headerfile must be included.There are three major families of functions that can be used to match a targettext against a regular expression. Each of these functions, as well as thematch_results::format member, has a finalstd::regex_constants::match_flag_type parameter (see the next section),which is given the default valueregex_constants::match_default which canbe used to fine-tune the way the regular expression and the matching processis being used. Thisfinal parameter is not explicitly mentioned with the regular expressionmatching functions or with theformat member. The three families offunctions are:
bool std::regex_match(Parameters):true is returned; otherwisefalse is returned. Refer to section18.8.4.2 for an overview of the available overloadedregex_match functions;bool std::regex_search(Parameters):false is returned. See below for an overview of the available overloadedregex_search functions;ReturnType std::regex_replace(Parameters):regex object and a format string. This member closely resembles the functionality of thematch_results::format member discussed in section18.8.4.4.match_results::format member can be used afterregex_replaceand is discussed after coveringregex_replace (section18.8.4.4).format members and all regular expression matchingfunctions accept a finalregex_constants::match_flag_type argument, which is abit-masked type, for which thebit_or operator can be used. Allformatmembers by default specify the argumentmatch_default.Thematch_flag_type enumeration defines the following values (below,`[first, last)' refers to the character sequence being matched).
format_default (not a bit-mask value, but a default value which isequal to 0). With just this specification ECMAScript rules are used toconstruct strings instd::regex_replace;format_first_only:std::regex_replace only replaces the firstmatch;format_no_copy: non-matching strings are not passed to the outputbystd::regex_replace;format_sed: POSIXsed(1) rules are used to construct strings instd::regex_replace;match_any: if multiple matches are possible, then any match is anacceptable result;match_continuous: sub-sequences are only matching if they start atfirst;match_not_bol: the first character in[first, last) is treatedas an ordinary character:^ does not match[first, first);match_not_bow:\b does not match[first, first);match_default (not a bit-mask value, but equal to 0): the default value of the final argument that's passed to the regular expression matching functions andmatch_results::format member. ECMAScriptrules are used to construct strings instd::regex_replace;match_not_eol: the last character in[first, last) is treatedas an ordinary character:$ does not match[last,last);match_not_eow:\b does not match[last, last);match_not_null: empty sequences are not considered matches;match_prev_avail:--first refers to a valid characterposition. When specifiedmatch_not_bol andmatch_not_bow areignored;std::regex_matchreturnstrue if the regular expression defined in its providedregex argumentfully matches the provided target text. This means thatmatch_results::prefix andmatch_results::suffix must return emptystrings. But defining sub-expressions is OK.The following overloaded variants of this function are available:
bool regex_match(BidirConstIter first, BidirConstIter last, std::match_results &results, std::regex const &re):BidirConstIter is a bidirectional const iterator. The range[first, last) defines the target text. The match results are returned inresults. The types of the iterators must match the type of thematch_results that's used. E.g., acmatch should be used if the iterators are ofchar const * types, and asmatch should be used if the iterators are ofstring::const_iterator types. Similar correspondence requirements hold true for the other overloaded versions of this function;bool regex_match(BidirConstIter first, BidirConstIter last, std::regex const &re):match_results object;bool regex_match(char const *target, std::match_results &results, std::regex const &re):target as its target text;bool regex_match(char const *str, std::regex const &re):bool regex_match(std::string const &target, std::match_results &results, std::regex const &re):target as its target text;bool regex_match(std::string const &str, std::regex const &re):bool regex_match(std::string const &&, std::match_results &, std::regex &) = delete (theregex_match function does not accept temporarystring objects as target strings, as this would result in invalid string iterators in thematch_result argument.)argv[1]) if it starts with 5 digits and then merely contains letters ([[:alpha:]]). The digits can be retrieved assub-expression 1: #include <iostream> #include <regex> using namespace std; int main(int argc, char const **argv) { regex re("(\\d{5})[[:alpha:]]+"); cmatch results; if (not regex_match(argv[1], results, re)) cout << "No match\n"; else cout << "size: " << results.size() << ": " << results.str(1) << " -- " << results.str() << '\n'; }regex_match the regular expression matching functionstd::regex_search returnstrue if the regularexpression defined in itsregex argument partially matches the targettext.The following overloaded variants of this function are available:
bool regex_search(BidirConstIter first, BidirConstIter last, std::match_results &results, std::regex const &re):BidirConstIter is a bidirectional const iterator. The range[first, last) defines the target text. The match results are returned inresults. The types of the iterators must match the type of thematch_results that's used. E.g., acmatch should be used if the iterators are ofchar const * types, and asmatch should be used if the iterators are ofstring::const_iterator types. Similar correspondence requirements hold true for the other overloaded versions of this function;bool regex_search(BidirConstIter first, BidirConstIter last, std::regex const &re):match_results object;bool regex_search(char const *target, std::match_results &results, std::regex const &re):target as its target text;bool regex_search(char const *str, std::regex const &re):bool regex_search(std::string const &target, std::match_results &results, std::regex const &re):target as its target text;bool regex_search(std::string const &str, std::regex const &re):bool regex_search(std::string const &&, std::match_results &, std::regex &) = delete:regex_search function does not accept temporarystring objects as target strings, as this would result in invalid string iterators in thematch_result argument.regex_search could be used: 1: #include <iostream> 2: #include <string> 3: #include <regex> 4: 5: using namespace std; 6: 7: int main() 8: { 9: while (true) 10: { 11: cout << "Enter a pattern or plain Enter to stop: "; 12: 13: string pattern; 14: if (not getline(cin, pattern) or pattern.empty()) 15: break; 16: 17: regex re(pattern); 18: while (true) 19: { 20: cout << "Enter a target text for `" << pattern << "'\n" 21: "(plain Enter for the next pattern): "; 22: 23: string text; 24: if (not getline(cin, text) or text.empty()) 25: break; 26: 27: smatch results; 28: if (not regex_search(text, results, re)) 29: cout << "No match\n"; 30: else 31: { 32: cout << "Prefix: " << results.prefix() << "\n" 33: "Match: " << results.str() << "\n" 34: "Suffix: " << results.suffix() << "\n"; 35: for (size_t idx = 1; idx != results.size(); ++idx) 36: cout << "Match " << idx << " at offset " << 37: results.position(idx) << ": " << 38: results.str(idx) << '\n'; 39: } 40: } 41: } 42: }match_results::format member is a rather complex memberfunction of the classmatch_results, which can be used to modify textwhich was previously matched against a regular expression, e.g., using thefunctionregex_search. Because of its complexity and because thefunctionality of another regular expression processing function(regex_replace) offers similar functionality it is discussed at this pointin theC++ Annotations, just before discussing theregex_replace function.Theformat member operates on (sub-)matches contained in amatch_results object, using aformat string, and producing text inwhich format specifiers (like$&) are replaced bymatching sections of the originally provided target text. In addition, theformat member recognizes all standardC escape sequences (like\n). Theformat member is used to create text that is modified withrespect to the original target text.
As a preliminary illustration: ifresults is amatch_results objectandmatch[0] (the fully matched text) equals `hello world', thencallingformat with the format stringthis is [$&] produces the textthis is [hello world]. Note the specification$& in this formatstring: this is an example of a format specifier. Here is an overview of allsupported format specifiers:
$`: corresponds to the text returned by theprefix member: all characters in the original target text up to the first character of the fully matched text;$&: corresponds to the fully matched text (i.e., the text returned by thematch_results::str member);$n: (wheren is an integral natural number): corresponds to the text returned buoperator[](n);$': corresponds to the text returned by thesuffix member: all characters in the original target string beyond the last character of the fully matched text;$$: corresponds to the single$ character.Four overloaded versions of theformat members are available. Alloverloaded versions define a finalregex_constants::match_flag_typeparameter, which is by default initialized tomatch_default. This finalparameter is not explicitly mentioned in the following coverage of theformat members.
To further illustrate the way theformat members can be used it is assumedthat the following code has been executed:
1: regex re("([[:alpha:]]+)\\s+(\\d+)"); // letters blanks digits 2: 3: smatch results; 4: string target("this value 1024 is interesting"); 5: 6: if (not regex_search(target, results, re)) 7: return 1; After callingregex_search (line 6) the results of the regularexpression matching process are available in thematch_results resultsobject that is defined in line 3.The first two overloadedformat functions expect an output-iterator towhere the formatted text is written. These overloaded members return the final output iterator, pointing just beyond the character that was lastwritten.
OutputIter format(OutputIter out, char const *first, char const *last) const:[first, last) are applied to the sub-expressions stored in thematch_results object, and the resulting string is inserted atout. An illustration is provided with the next overloaded version;OutputIter format(OutputIter out, std::string const &fmt) const:fmt is applied to the sub-expressions stored in thematch_results object, and the resulting string is inserted atout. The next line of code inserts the value 1024 intocout (note thatfmtmust be astd::string, hence the explicit use of thestring constructor):results.format(ostream_iterator<char>(cout, ""), "$2"s);
The remaining two overloadedformat members expect astd::string oran NTBS defining the format string. Both members return astd::stringcontaining the formatted text:
std::string format(std::string const &fmt) conststd::string format(char const *fmt) conststring can be obtainedin which the order of the first and second marked sub-expressions contained inthe previously obtainedmatch_results object have beenswapped: string reverse(results.format("$2 and $1"));std::regex_replace functions uses regularexpressions to perform substitution on sequences of characters. Theirfunctionality closely resembles the functionality of thematch_results::format member discussed in the previous section. Thefollowing overloaded variants are available:OutputIt regex_replace(OutputIter out, BidirConstIter first, BidirConstIter last, std::regex const &re, std::string const &fmt):OutputIter is an output iterator;BidirConstIter a bidirectional const iterator.The function returns the possibly modified text in an iterator range[out, retvalue), whereout is the output iterator passed as the first argument toregex_replace, andretvalue is the output iterator returned byregex_replace.
The function matches the text at the range[first, last) against the regular expression stored inre. If the regular expression doesnot match the target text in the range[first, last) then the target text is literally copied toout. If the regular expressiondoes match the target text then
out. The prefix equals the initial characters of the target text up to the very first character of the fully matched text.fmt format string, in which the format specifiers can be used that were described in the previous section (section18.8.4.4), and the replaced text is copied toout;out. The suffix equals all characters of the target text beyond the last character of the matched text.regex_replace is illustrated in the next example: 1: regex re("([[:alpha:]]+)\\s+(\\d+)"); // letters blanks digits 2: 3: string target("this value 1024 is interesting"); 4: 5: regex_replace(ostream_iterator<char>(cout, ""), target.begin(), 6: target.end(), re, "$2"s); In line 5regex_replace is called. Its format string merely contains$2, matching 1024 in the target text. The prefix ends at the wordvalue, the suffix starts beyond 1024, so the statement in line 5 inserts the textthis 1024 is interesting
into the standard output stream.
OutputIt regex_replace( OutputIter out, BidirConstIter first, BidirConstIter last, std::regex const &re, char const *fmt):"$2" instead of"$2"s, then this variant would have been used;std::string regex_replace(std::string const &str, std::regex const &re, std::string const &fmt):std::string containing the modified text, and expects astd::string containing the target text. Other than that, it behaves like the first variant. To use this overloaded variant in the above example the statement in line 5 could have been replaced by the following statement, initializing thestring result:string result(regex_replace(target, re, "$2"s));
std::string regex_replace(std::string const &str, std::regex const &re, char const *fmt):"$2"s into"$2", this variant is used, behaving exactly like the previous variant;std::string regex_replace(char const *str, std::regex const &re, std::string const &fmt):char const * to point to the target text, and behaves exactly like the previous but one variant;std::string regex_replace(char const *str, std::regex const &re, char const *fmt):char const * to point to the target text, and also behaves exactly like the previous but one variant;<random> header file must be included.The STL offers several standard mathematical (statistical)distributions. These distributions allow programmers to obtain randomlyselected values from a selected distribution.
These statistical distributions need to be provided with a random numbergenerating object. Several of such random number generating objects areprovided, extending the traditionalrand function that is part of theC standard library.
These random number generating objects produce pseudo-random numbers, whichare then processed by the statistical distribution to obtain values that arerandomly selected from the specified distribution.
Although the STL offers various statistical distributions their functionalityis fairly limited. The distributions allow us to obtain a random number fromthese distributions, butprobability density functions orcumulative distribution functions are currently not provided by the STL. These functions (distributions as wellas the density and the cumulative distribution functions) are, however,available in other libraries, like theboost math library (specifically:
http://www.boost.org/doc/libs/1_44_0/libs/math/doc/sf_and_dist/html/index.html).
It is beyond the scope of theC++ Annotations to discuss the mathematicalcharacteristics of the various statistical distributions. The interestedreader is referred to the pertinent mathematical textbooks (like Stuart andOrd's (2009)Kendall's Advanced Theory of Statistics, Wiley) or to web-locationslikehttp://en.wikipedia.org/wiki/Bernoulli_distribution.
Thelinear_congruential_engine random number generator computes
valuei+1 = (+a * valuei + c+) % ma; the additive constantc; and the modulo valuem. Example:linear_congruential_engine<int, 10, 3, 13> lincon;
Thelinear_congruential generator may be seeded by providing itsconstructor with a seeding-argument. E.g.,lincon(time(0)).
Thesubtract_with_carry_engine random number generator computes
valuei = (valuei-s - valuei-r - carryi-1) % mm; and the subtractiveconstantss andr. Example:subtract_with_carry_engine<int, 13, 3, 13> subcar;
Thesubtract_with_carry_engine generator may be seeded by providingits constructor with a seeding-argument. E.g.,subcar(time(0)).
The predefinedmersenne_twister_engine mt19937 (predefined as type in the<random> header file) is used in the examples below. It can be constructedusing`mt19937 mt' or it can be seeded by providing itsconstructor with an argument (e.g.,mt19937 mt(time(0))). Its functioncall operator returns a random unsigned integral value.
Other ways to initialize themersenne_twister_engine are beyond thescope of theC++ Annotations (but see Lewisetal. ( Lewis, P.A.W., Goodman, A.S., and Miller, J.M. (1969), A pseudorandomnumber generator for the System/360, IBM Systems Journal, 8, 136-146.) (1969)).
The random number generators may also be seeded by calling their membersseed acceptingunsigned long values or generator functions (as inlc.seed(time(0)), lc.seed(mt)).
The random number generators offer membersmin andmaxreturning, respectively, their minimum and maximum values (inclusive). If areduced range is required the generators can be nested in a function or classadapting the range.
Here's a small example showing how themersenne_twister_enginemt19937 can be used to generate random numbers:
#include <iostream> #include <ctime> #include <random> using namespace std; // arguments: 1st: number of random numbers to generate // 2nd: lowest positve random number, // 3rd: highest positive random number int main(int argc, char **argv) { mt19937 mt( time(0) ); // seed with the current time in secs. for ( size_t nGenerate = stoul(argv[1]), lowest = stoul(argv[2]), mod = stoul(argv[3]) + 1 - lowest; nGenerate--; ) cout << (lowest + mt() % mod) << ' '; cout << '\n'; }RNG is used toindicate aRandom Number Generator andURNG is used to indicate aUniform Random Number Generator. With each distribution astruct param_type is defined containing the distribution's parameters. Theorganization of theseparam_type structs depends on (and is describedat) the actual distribution.All distributions offer the following members (result_type refers tothe type name of the values returned by the distribution):
result_type max() constresult_type min() constparam_type param() constparam_type struct;void param(const param_type ¶m) redefines the parameters of the distribution;void reset(): clears all of its cached values;All distributions support the following operators (distribution-name should be replaced by the name of the intended distribution, e.g.,normal_distribution):
template<typename URNG> result_type operator()(URNG &urng)urng returning the next random number selected from a uniform random distribution;template<typename URNG> result_type operator()(URNG &urng, param_type ¶m)param struct. The function objecturng returns the next random number selected from a uniform random distribution;std::istream &operator>>(std::istream &in, distribution-name &object): The parameters of the distribution are extracted from anstd::istream;std::ostream &operator<<(std::ostream &out, distribution-name const &bd): The parameters of the distribution are inserted into anstd::ostreamThe following example shows how the distributions can be used. Replacingthe name of the distribution (normal_distribution) by anotherdistribution's name is all that is required to switch distributions. Alldistributions have parameters, like the mean and standard deviation of thenormal distribution, and all parameters have default values. The names of theparameters vary over distributions and are mentioned below at the individualdistributions. Distributions offer members returning or setting theirparameters.
Most distributions are defined as class templates, requiring the specificationof a data type that is used for the function's return type. If so, an emptytemplate parameter type specification (<>) will get you the defaulttype. The default types are eitherdouble (for real valued return types)orint (for integral valued return types). The template parameter typespecification must be omitted with distributions that are not defined astemplate classes.
Here is an example showing the use of the statistical distributions, appliedto the normal distribution:
#include <iostream>#include <ctime>#include <random>using namespace std;int main(){ std::mt19937 engine(time(0)); std::normal_distribution<> dist; for (size_t idx = 0; idx < 10; ++idx) std::cout << "a random value: " << dist(engine) << "\n"; cout << '\n' << dist.min() << " " << dist.max() << '\n';}bernoulli_distribution is used to generate logical truth (boolean)values with a certain probabilityp. It is equal to a binomialdistribution for one experiment (cf18.9.2.2).The bernoulli distribution isnot defined as a class template.
Defined types:
using result_type = bool; struct param_type { explicit param_type(double prob = 0.5); double p() const; // returns prob };Constructor and members:
bernoulli_distribution(double prob = 0.5)prob of returningtrue;double p() constprob;result_type min() constfalse;result_type max() consttrue;binomial_distribution<IntType = int> is used to determine theprobability of the number of successes in a sequence ofn independentsuccess/failure experiments, each of which yields success with probabilityp.The template type parameterIntType defines the type of the generatedrandom value, which must be an integral type.
Defined types:
using result_type = IntType; struct param_type { explicit param_type(IntType trials, double prob = 0.5); IntType t() const; // returns trials double p() const; // returns prob };Constructors and members and example:
binomial_distribution<>(IntType trials = 1, double prob = 0.5) constructs a binomial distribution fortrials experiments, each having probabilityprob of success.binomial_distribution<>(param_type const ¶m) constructs a binomial distribution according to the values stored in theparam struct.IntType t() consttrials;double p() constprob;result_type min() constresult_type max() consttrials;cauchy_distribution<RealType = double> looks similar to a normaldistribution. But cauchy distributions have heavier tails. When studyinghypothesis tests that assume normality, seeing how the tests perform on datafrom a Cauchy distribution is a good indicator of how sensitive the tests areto heavy-tail departures from normality.The mean and standard deviation of the Cauchy distribution are undefined.
Defined types:
using result_type = RealType; struct param_type { explicit param_type(RealType a = RealType(0), RealType b = RealType(1)); double a() const; double b() const; };Constructors and members:
cauchy_distribution<>(RealType a = RealType(0), RealType b = RealType(1)) constructs a cauchy distribution with specifieda andb parameters.cauchy_distribution<>(param_type const ¶m) constructs a cauchy distribution according to the values stored in theparam struct.RealType a() consta parameter;RealType b() constb parameter;result_type min() constresult_type value;result_type max() constresult_type;chi_squared_distribution<RealType = double> withn degrees offreedom is the distribution of a sum of the squares ofn independentstandard normal random variables.Note that even though the distribution's parametern usually is anintegral value, it doesn't have to be integral, as the chi_squareddistribution is defined in terms of functions (exp andGamma) thattake real arguments (see, e.g., the formula shown in the<bits/random.h>header file, provided with the GNUg++ compiler distribution).
The chi-squared distribution is used, e.g., when testing the goodness of fitof an observed distribution to a theoretical one.
Defined types:
using result_type = RealType; struct param_type { explicit param_type(RealType n = RealType(1)); RealType n() const; };Constructors and members:
chi_squared_distribution<>(RealType n = 1) constructs a chi_squared distribution with specified number of degrees of freedom.chi_squared_distribution<>(param_type const ¶m) constructs a chi_squared distribution according to the value stored in theparam struct;IntType n() constresult_type min() constresult_type max() constresult_type;extreme_value_distribution<RealType = double> is related to theWeibull distribution and is used in statistical models where the variable ofinterest is the minimum of many random factors, all of which can take positiveor negative values.It has two parameters: a location parametera and scale parameterb.See also
http://www.itl.nist.gov/div898/handbook/apr/section1/apr163.htm
Defined types:
using result_type = RealType;struct param_type{ explicit param_type(RealType a = RealType(0), RealType b = RealType(1)); RealType a() const; // the location parameter RealType b() const; // the scale parameter};Constructors and members:
extreme_value_distribution<>(RealType a = 0, RealType b = 1) constructs an extreme value distribution with specifieda andb parameters;extreme_value_distribution<>(param_type const ¶m) constructs an extreme value distribution according to the values stored in theparam struct.RealType a() constRealType stddev() constresult_type min() constresult_type;result_type max() constresult_type;exponential_distribution<RealType = double> is used to describe thelengths between events that can be modeled with a homogeneous Poissonprocess. It can be interpreted as the continuous form of thegeometric distribution.Its parameterprob defines the distribution'slambda parameter, calleditsrate parameter. Its expected value and standard deviation are both1 / lambda.
Defined types:
using result_type = RealType; struct param_type { explicit param_type(RealType lambda = RealType(1)); RealType lambda() const; };Constructors and members:
exponential_distribution<>(RealType lambda = 1) constructs an exponential distribution with specifiedlambda parameter.exponential_distribution<>(param_type const ¶m) constructs an exponential distribution according to the value stored in theparam struct.RealType lambda() constlambda parameter;result_type min() constresult_type max() constresult_type;fisher_f_distribution<RealType = double> is intensively used instatistical methods like the Analysis of Variance. It is the distributionresulting from dividing twoChi-squared distributions.It is characterized by two parameters, being the degrees of freedom of the twochi-squared distributions.
Note that even though the distribution's parametern usually is anintegral value, it doesn't have to be integral, as the Fisher F distributionis constructed from Chi-squared distributions that accept a non-integralparameter value (see also section18.9.2.4).
Defined types:
using result_type = RealType; struct param_type { explicit param_type(RealType m = RealType(1), RealType n = RealType(1)); RealType m() const; // The degrees of freedom of the nominator RealType n() const; // The degrees of freedom of the denominator };Constructors and members:
fisher_f_distribution<>(RealType m = RealType(1), RealType n = RealType(1)) constructs a fisher_f distribution with specified degrees of freedom.fisher_f_distribution<>(param_type const ¶m) constructs a fisher_f distribution according to the values stored in theparam struct.RealType m() constRealType n() constresult_type min() constresult_type max() constresult_type;gamma_distribution<RealType = double> is used when working with datathat are not distributed according to the normal distribution. It is oftenused to model waiting times.It has two parameters,alpha andbeta. Its expected value isalpha* beta and its standard deviation isalpha * beta2.
Defined types:
using result_type = RealType; struct param_type { explicit param_type(RealType alpha = RealType(1), RealType beta = RealType(1)); RealType alpha() const; RealType beta() const; };Constructors and members:
gamma_distribution<>(RealType alpha = 1, RealType beta = 1) constructs a gamma distribution with specifiedalpha andbeta parameters.gamma_distribution<>(param_type const ¶m) constructs a gamma distribution according to the values stored in theparam struct.RealType alpha() constalpha parameter;RealType beta() constbeta parameter;result_type min() constresult_type max() constresult_type;geometric_distribution<IntType = int> is used to model the numberof bernoulli trials (cf.18.9.2.1) needed until the first success.It has one parameter,prob, representing the probability of success in anindividual bernoulli trial.
Defined types:
using result_type = IntType; struct param_type { explicit param_type(double prob = 0.5); double p() const; };Constructors, members and example:
geometric_distribution<>(double prob = 0.5) constructs a geometric distribution for bernoulli trials each having probabilityprob of success.geometric_distribution<>(param_type const ¶m) constructs a geometric distribution according to the values stored in theparam struct.double p() constprob parameter;param_type param() constparam_type structure;void param(const param_type ¶m) redefines the parameters of the distribution;result_type min() const0);result_type max() consttemplate<typename URNG> result_type operator()(URNG &urng)template<typename URNG> result_type operator()(URNG &urng, param_type ¶m)param struct.#include <iostream>#include <ctime>#include <random>int main(){ std::linear_congruential_engine<unsigned, 7, 3, 61> engine(0); std::geometric_distribution<> dist; for (size_t idx = 0; idx < 10; ++idx) std::cout << "a random value: " << dist(engine) << "\n"; std::cout << '\n' << dist.min() << " " << dist.max() << '\n';}lognormal_distribution<RealType = double> is a probabilitydistribution of a random variable whose logarithm is normally distributed. Ifa random variableX has a normal distribution, thenY = eX has alog-normal distribution.It has two parameters,m ands representing, respectively, the meanand standard deviation ofln(X).
Defined types:
using result_type = RealType; struct param_type { explicit param_type(RealType m = RealType(0), RealType s = RealType(1)); RealType m() const; RealType s() const; };Constructor and members:
lognormal_distribution<>(RealType m = 0, RealType s = 1) constructs a log-normal distribution for a random variable whose mean and standard deviation is, respectively,m ands.lognormal_distribution<>(param_type const ¶m) constructs a log-normal distribution according to the values stored in theparam struct.RealType m() constm parameter;RealType stddev() consts parameter;result_type min() constresult_type max() constresult_type;normal_distribution<RealType = double> is commonly used in science todescribe complex phenomena. When predicting or measuring variables, errors arecommonly assumed to be normally distributed.It has two parameters,mean andstandard deviation.
Defined types:
using result_type = RealType; struct param_type { explicit param_type(RealType mean = RealType(0), RealType stddev = RealType(1)); RealType mean() const; RealType stddev() const; };Constructors and members:
normal_distribution<>(RealType mean = 0, RealType stddev = 1) constructs a normal distribution with specifiedmean andstddev parameters. The default parameter values define thestandard normal distribution;normal_distribution<>(param_type const ¶m) constructs a normal distribution according to the values stored in theparam struct.RealType mean() constmean parameter;RealType stddev() conststddev parameter;result_type min() constresult_type;result_type max() constresult_type;negative_binomial_distribution<IntType = int> probability distributiondescribes the number of successes in a sequence of Bernoulli trials before aspecified number of failures occurs. For example, if one throws a dierepeatedly until the third time 1 appears, then the probability distributionof the number of other faces that have appeared is a negative binomialdistribution.It has two parameters: (IntType) k (> 0), being the number of failuresuntil the experiment is stopped and (double) p the probability of successin each individual experiment.
Defined types:
using result_type = IntType; struct param_type { explicit param_type(IntType k = IntType(1), double p = 0.5); IntType k() const; double p() const; };Constructors and members:
negative_binomial_distribution<>(IntType k = IntType(1), double p = 0.5) constructs a negative_binomial distribution with specifiedk andp parameters;negative_binomial_distribution<>(param_type const ¶m) constructs a negative_binomial distribution according to the values stored in theparam struct.IntType k() constk parameter;double p() constp parameter;result_type min() constresult_type max() constresult_type;poisson_distribution<IntType = int> is used to model the probabilityof a number of events occurring in a fixed period of time if these eventsoccur with a known probability and independently of the time since the lastevent.It has one parameter,mean, specifying the expected number of events inthe interval under consideration. E.g., if on average 2 events are observed ina one-minute interval and the duration of the interval under study is10 minutes thenmean = 20.
Defined types:
using result_type = IntType; struct param_type { explicit param_type(double mean = 1.0); double mean() const; };Constructors and members:
poisson_distribution<>(double mean = 1) constructs a poisson distribution with specifiedmean parameter.poisson_distribution<>(param_type const ¶m) constructs a poisson distribution according to the values stored in theparam struct.double mean() constmean parameter;result_type min() constresult_type max() constresult_type;student_t_distribution<RealType = double> is a probabilitydistribution that is used when estimating the mean of a normally distributedpopulation from small sample sizes.It is characterized by one parameter: the degrees of freedom, which is equalto the sample size - 1.
Defined types:
using result_type = RealType; struct param_type { explicit param_type(RealType n = RealType(1)); RealType n() const; // The degrees of freedom };Constructors and members:
student_t_distribution<>(RealType n = RealType(1)) constructs a student_t distribution with indicated degrees of freedom.student_t_distribution<>(param_type const ¶m) constructs a student_t distribution according to the values stored in theparam struct.RealType n() constresult_type min() constresult_type max() constresult_type;uniform_int_distribution<IntType = int> can be used to select integralvalues randomly from a range of uniformly distributed integral values.It has two parameters,a andb, specifying, respectively, the lowestvalue that can be returned and the highest value that can be returned.
Defined types:
using result_type = IntType; struct param_type { explicit param_type(IntType a = 0, IntType b = max(IntType)); IntType a() const; IntType b() const; };Constructors and members:
uniform_int_distribution<>(IntType a = 0, IntType b = max(IntType)) constructs a uniform_int distribution for the specified range of values.uniform_int_distribution<>(param_type const ¶m) constructs a uniform_int distribution according to the values stored in theparam struct.IntType a() consta parameter;IntType b() constb parameter;result_type min() consta parameter;result_type max() constb parameter;uniform_real_distribution<RealType = double> can be used to selectRealType values randomly from a range of uniformly distributedRealType values.It has two parameters,a andb, specifying, respectively, thehalf-open range of values ([a, b)) that can be returned by thedistribution.
Defined types:
using result_type = RealType; struct param_type { explicit param_type(RealType a = 0, RealType b = max(RealType)); RealType a() const; RealType b() const; };Constructors and members:
uniform_real_distribution<>(RealType a = 0, RealType b = max(RealType)) constructs a uniform_real distribution for the specified range of values.uniform_real_distribution<>(param_type const ¶m) constructs a uniform_real distribution according to the values stored in theparam struct.RealType a() consta parameter;RealType b() constb parameter;result_type min() consta parameter;result_type max() constb parameter;weibull_distribution<RealType = double> is commonly used inreliability engineering and in survival (life data) analysis.It has two or three parameters and the two-parameter variant is offered by theSTL. The three parameter variant has a shape (or slope) parameter, a scaleparameter and a location parameter. The two parameter variant implicitly usesthe location parameter value 0. In the two parameter variant the shapeparameter (a) and the scale parameter (b) are provided. See
http://www.weibull.com/hotwire/issue14/relbasics14.htm for aninteresting coverage of the meaning of the Weibull distribution's parameters.
Defined types:
using result_type = RealType; struct param_type { explicit param_type(RealType a = RealType{ 1 }, RealType b = RealType{ 1 }); RealType a() const; // the shape (slope) parameter RealType b() const; // the scale parameter };Constructors and members:
weibull_distribution<>(RealType a = 1, RealType b = 1) constructs a weibull distribution with specifieda andb parameters;weibull_distribution<>(param_type const ¶m) constructs a weibull distribution according to the values stored in theparam struct.RealType a() constRealType stddev() constresult_type min() constresult_type max() constresult_type;structs, std::pair or (cf. section22.6)tuples) as local variables inside functions. A basic example usingstructured bindings is shown in the following code snippet: pair<int, int> factory() { return { 1, 2 }; } void fun() { auto [one, two] = factory(); cout << one << ' ' << two << '\n'; }Being able to use structured bindings is very useful in cases like these.
But what if we want to assign the fields of a struct to variables that havealready been defined or that were passed to a function via its parameters? Inthose situations structured bindings offer no help. E.g., in the followingcode snippet a functionretrieve is defined having anint & parameterand anint local variable and we want to assign the values returned byfactory to those variables:
void retrieve(int &one) { int two; // ... = factory() ?? }Structured bindings cannot be used here: the elements of structured bindingscannot be references. Although itis possible to define astd::pair<int&, int &> such an object cannot be initialized with the references ofoneandtwo which are directly referring to the fields returned byfactory. These statements won't compile:
pair<int &, int &> p{one, two} = factory(); pair<int &, int &>{one, two} = factory();While it is possible to first define apair<int &, int &> object and thenassignfactory's return value to it, that approach clearly is less elegantthan what's offered by structured bindings:
pair<int &, int &> p{one, two}; p = factory();Fortunately, there is a better alternative. After including the<tuple>header file (see also section22.6)std::tie is available allowingus to `tie' references to fields of structured data types. Usingstd::tieit is very easy to associate the variablesone andtwo of the functionretrieve with the fields of the pair returned byfactory:
void retrieve(int &one) { int two; tie(one, two) = factory(); cout << one << ' ' << two << '\n'; }When Executing these statements:
int one = 0; int two = 0; cout << one << ' ' << two << '\n'; retrieve(one); cout << one << ' ' << two << '\n';
the following output is obtained:
0 0 1 2 1 0
In addition to the above thestd::tie function also supports ordering and(in)equality comparisons. Thestruct Data in the next example definesthree fields: anint, astd::string and adouble. Each of thesefields support ordering and (in)equality comparisons. In those cases, allcomparison operators can easily be implemented through the spaceshipoperator (cf. section11.7.2) usingstd::tie:
struct Data { int d_int; string d_string; double d_double; }; bool operator==(Data const &lhs, Data const &rhs) { return tie(lhs.d_int, lhs.d_string, lhs.d_double) == tie(rhs.d_int, rhs.d_string, rhs.d_double); } partial_ordering operator<=>(Data const &lhs, Data const &rhs) { return tie(lhs.d_int, lhs.d_string, lhs.d_double) <=> tie(rhs.d_int, rhs.d_string, rhs.d_double); }Note thatstruct Data's spaceship operator returnspartial_orderingvalues (cf. section18.7.3). Althoughint andstd::string'sspaceship operators returnstrong_ordering values,double's spaceshipoperator doesn't. Instead it returnspartial_orderingvalues. Consequently,struct Data's spaceship operator also returnspartial_ordering values.
std::optional objects the<optional> header file mustbe included.Consider a function returning subsequent lines from a stream. That functioncould be a member function reading from a stream which was opened by itsobject. A first implementation of such a member function could be
std::string Class::nextLine() { string line; getline(d_stream, line); return line; } Of course, this implementation is sub-optimal asgetline may fail.Common ways to handle failures in these situations are
getline fails, and point to strings containing the lines ifgetline succeeds;getline succeeded;std::pair orstd::tuple objects of which one field is abool and the other field is astd::string.The standard template library offers yet another way to handle situations likethese: the template class
template <typename DataType> class optional;Here,
DataType refers to the name of the data type that is handled bytheoptional class. Instead of returning astd::string the functionnextLine may specify astd::optional<std::string> return type:std::optional<std::string> Class::nextLine().The interpretation ofstd::optional objects is simple: either it containsan object of itsDataType or it doesn't. If itdoes contain aDataType object then that object is available as object instead of apointer to an object (which might have been dynamically allocated) of thespecifiedDataType. At the same type theoptional object can beinterpreted as abool. If theoptional object contains aDataTypeobject theoptional's bool value istrue. If it doesn't contain aDataType value, then itsbool value isfalse.
The classstd::optional offers the following facilities:
std::optional<std::string> opt;) does not contain a value;DataType (e.g., anoptional<string> can be initialized from a NTBS). If the initializing value is an rvalue reference then theDataType object is move-constructed from the initializing value;operator=DataType values ofoptional objects or to reassign theoptional objects from anotheroptional object using the sameDataType. Copy- and move-assignment operators are available;explicit operator bool() and thehas_value() members returntrue if theoptional object contains aDataType value;value(), operator*(), andoperator->() return references to the optional'sDataType value. The reference is aconst reference if called from anoptional<DataType> const object; it is an rvalue-reference if called from an rvalue reference to anoptional<DataType> object. Notes:operator* andoperator-> members act likevalue, but do not imply that theoptional's DataType member itself is stored as a pointer;value() checks whether theoptional object actually contains aDataType object, and throws astd::bad_optional_access exception if not.value_or(Type &&defaultValue) returns a copy of theoptional object'sDataType if the object contains a value or returnsDataType{ defaultValue } if not. Note thatDataType must be constructible fromType;
swap(optional<DataType> &other): swaps the current and otheroptional objects' content.reset(): erases theoptional's DataType member. Followingreset() has_value() returnsfalse.emplace(Args &&...args), emplace(initialize_list, Args &&...args): the firstemplace forwardsargs toDataType's constructor; the secondemplace forwardsargs to the initializer_list, and forwards that list toDataType's constructor;operator<=>) are available (if defined for theoptional's DataType) to compare theDataType objects of twooptional objects;std::optional<DataType> make_optional(...)optional object constructed from aDataType lvalue or rvalue reference, or constructed from the same arguments that are accepted byemplace.Here is the implementation of a functionnextLine, usingstd::optional<std::string> and a simplemain function illustrating itsworkings:
#include <iostream> #include <sstream> #include <string> #include <optional> using namespace std; optional<string> nextLine(istream &in) { std::optional<std::string> opt; string line; if (getline(in, line)) opt = move(line); cout << "internal: has value: " << opt.has_value() << ", value = " << *opt << '\n'; return opt; } int main() { istringstream in{ "hello world\n" }; auto opt = nextLine(in); cout << "main: has value: " << opt.has_value() << ", value = " << *opt << '\n'; opt = nextLine(in); cout << "main: has value: " << opt.has_value() << ", value = " << *opt << '\n'; }The ouput of this program is:
internal: has value: 1, value = hello world main: has value: 1, value = hello world internal: has value: 0, value = main: has value: 0, value = hello worldNote that after the 2nd call, when no value is returned,
opt has kept thevalue it received at the first call:optional's assignment operatordoesn't bother about values already present in their objects once it noticesthathas_value will returnfalse. So be sure to inspecthas_valueoroperator bool before callingvalue.