© Peter Van Weert and Marc Gregoire 2016
Peter Van Weert and Marc GregoireC++ Standard Library Quick Reference10.1007/978-1-4842-1876-1_2

2. General Utilities

Peter Van Weert and Marc Gregoire2
(1)
Kessel-Lo, Belgium
(2)
Meldert, Belgium
 

Moving, Forwarding, Swapping<utility>

This section explains move(), move_if_noexcept(), forward(), swap(), and exchange(). In passing, it also introduces the concepts of move semantics and perfect forwarding.

Moving

An object can be moved elsewhere (rather than copied) if its previous user no longer needs it. Moving the resources from one object to another can often be implemented far more efficiently than (deep) copying them. For a string object, for instance, moving is typically as simple as copying a char* pointer and a length (constant time); there is no need to copy the entire char array (linear time).
Unless otherwise specified, the source object that was moved from is left in an undefined but valid state and should not be used anymore unless reinitialized. A valid implementation for moving a std::string (see Chapter 6), for instance, could set the source’s char* pointer to nullptr to prevent the array from being deleted twice, but this is not required by the Standard. Likewise, it is unspecified what length() will return after being moved from. Certain operations, assignments in particular, remain allowed, as demonstrated in the following example:
A417649_1_En_2_Figa_HTML.gif
Despite its name, the std::move() function technically does not move anything: instead, it simply marks that a given T, T&, or T&& value may be moved, effectively by statically casting it to an rvalue reference T&&. Because of the type cast, other functions may get selected by overload resolution, and/or value parameter objects may become initialized using their move constructors (of form T(T&& t)), if available, rather than their copy constructors. This initialization occurs at the callee side, not the caller side. An rvalue parameter T&& forces the caller to always move.
Similarly, an object can also be moved to another using a move assignment operator (of form operator=(T&&)):
A417649_1_En_2_Figb_HTML.gif
If no move member is defined, either explicitly or implicitly, overload resolution for T&& falls back to T& or T, and in the latter case still creates a copy. Conditions for implicit move members to be generated include that there may not be any user-defined copy, move, or destructor members, nor any non-static member variable or base class that cannot be moved.
The move_if_noexcept() function is similar to move(), except that it only casts to T&& if the move constructor of T is known not to throw from its exceptions specification (noexcept, or the deprecated throw()); otherwise, it casts to const T&.
All classes defined by the Standard have move members if appropriate. Many containers from Chapter 3, for example, can be moved in constant time (not std::array, although it will move individual elements if possible to avoid deep copies).
Tip
For optimal performance with nontrivial custom types, it is not only crucial to define move members, but also equally important to always do so with a noexcept specifier. The container classes from Chapter 3 extensively use moving to speed up operations such as adding a new element, or when relocating arrays of elements (for example, with sequential containers). Similarly, many algorithms from Chapter 4 benefit if efficient move members are provided (and/or nonmember swap() operations, discussed later). However, and especially when moving arrays of elements, these optimizations often take effect only if the values’ move members are known not to throw.

Forwarding

The std::forward() helper function is intended to be used in templated functions to efficiently pass its arguments along to other functions while preserving any move semantics. If the argument to forward<T>() was an lvalue reference T&, this reference is returned unchanged. Otherwise, the argument is cast to an rvalue reference T&&. An example will clarify its intended use:
A417649_1_En_2_Figc_HTML.gif
The idiom used by good_fwd() is called perfect forwarding . It optimally preserves rvalue references (such as those of std::move()d or temporary objects). The idiom’s first ingredient is a so-called forwarding or universal reference : a T&& parameter, with T a template type parameter. Without it, template argument deduction removes all references: for ugly_fwd() ; both A& and A&& become A. With a forwarding reference, A& and A&& are deduced, respectively: that is, even though the forwarding reference looks like T&&, if passed A&, A& is deduced and not A&&. Still, using a forwarding reference alone is not enough, as shown with bad_fwd(). When using the named variable t as is, it binds with an lvalue function parameter (all named variables do), even if its type is deduced as A&&. This is where std::forward<T>() comes in. Similar to std::move(), it casts to T&&, but only if given a value with an rvalue type (including named variables of type A&&).
All this is quite subtle and is more about the C++ language (type deduction in particular) than the Standard Library. The main takeaway here is that to correctly forward arguments of a function template to a function, you should consider using perfect forwarding—that is, a forwarding reference combined with std::forward().

Swapping

The std::swap() template function swaps two objects as if implemented as:
template<typename T> void swap(T& one, T& other)
{ T temp(std::move(one)); one = std::move(other); other = std::move(temp); }
A similar swap() function template to piecewise swap all elements of equally long T[N] arrays is defined as well.
Although already quite efficient if proper move members are available, for truly optimal performance you should consider specializing these template functions: for instance, to eliminate the need to move to a temporary. Many algorithms from Chapter 4, for example, call this non-member swap() function. For Standard types, swap() specializations are already defined where appropriate.
A function similar to swap() is std::exchange(), which assigns a new value to something while returning its old value. A valid implementation is
template<typename T, typename U=T> T exchange(T& x, U&& new_val)
  { T old_val(std::move(x)); x = std::forward<U>(new_val); return old_val; }
Tip
Although swap() and exchange() may be specialized in the std namespace, most recommend specializing them in the same namespace as their template argument type. The advantage then is that so-called argument-dependent lookup (ADL) works. In other words, that for instance swap(x,y) works without using directives or declarations and without specifying the namespace of swap(). The ADL rules basically stipulate that a non-member function should be looked up first in the namespace of its arguments. Generic code should then use the following idiom to fall back to std::swap() if need be: using std::swap; swap(x,y);. Simply writing std::swap(x,y) will not use user-defined swap() functions outside the std namespace, whereas swap(x,y) alone will not work unless there is such a user-defined function.

Pairs and Tuples

Pairs <utility>

The std::pair<T1,T2> template struct is a copyable, moveable, swappable, (lexicographically) comparable struct that stores a pair of T1 and T2 values in its public first and second member variables. A default-constructed pair zero-initializes its values, but initial values may be provided as well:
std::pair<unsigned int, Person> p(42u, Person("Douglas", "Adams"));
The two template type parameters can be deduced automatically using auxiliary function std::make_pair() :
auto p = std::make_pair(42u, Person("Douglas", "Adams"));
Tip
Not all types can be moved efficiently, and would have to be copied when constructing a pair. For bigger objects (e.g. those that contain fixed-size arrays), this could be a performance issue. Other types may even not be copyable at all. For such cases, std::pair has a special 'piecewise' constructor to perform in-place construction of its two members. It is called with a special constant, followed by two tuples (see next section) containing the arguments to forward to the constructors of both members.
For instance (forward_as_tuple() is used to not copy the strings to a temporary tuple):
   std::pair<unsigned, Person> p(std::piecewise_construct,
      std::make_tuple(42u), std::forward_as_tuple("Douglas", "Adams"));
Piecewise construction can also be used with the emplace() functions of the containers in Chapter 3 (these functions are similarly defined to avoid unwanted copying), and in particular with those of std::map and std::unordered_map.

Tuples <tuple>

std:: tuple is a generalization of pair that allows any number of values to be stored (that is, zero or more, not just two): std::tuple<Type...>. It is mostly analogous to pair, including the make_tuple() auxiliary function. The main difference is that the individual values are not stored in public member variables. Instead, you can access them using one of the get() template functions:
A417649_1_En_2_Figd_HTML.gif
An alternative way to obtain values of a tuple is by unpacking it using the tie() function. The special std::ignore constant may be used to exclude any value:
int one, two; double three;
std::tie(one, two, three, std::ignore) = t;
Tip
The std::tie() function may be used to compactly implement lexicographical comparisons based on multiple values. For instance, the body of operator< for the Person class in the Introduction could be written as
return std::tie(lhs.m_isVIP, lhs.m_lastName, lhs.m_firstName)
     < std::tie(rhs.m_isVIP, rhs.m_lastName, rhs.m_firstName);
Two helper structs exist to obtain the size and element types of a given tuple as well, which is mainly useful when writing generic code:
A417649_1_En_2_Fige_HTML.gif
Note that get(), tuple_size, and tuple_element are also defined for pair and std::array (see Chapter 3) in their respective headers, but not tie().
A final helper function for tuples is std::forward_as_tuple(), which creates a tuple of references to its arguments. These are lvalue references generally, but rvalue references are maintained, as with std::forward() explained earlier. It is designed to forward arguments (that is, while avoiding copies) to the constructor of a tuple, in particular in the context of functions that accept a tuple by value. A function f(tuple<std::string, int>), for instance, can then be called as follows: f(std::forward_as_tuple("test", 123));.
Tuples offer facilities for custom allocators as well, but this is an advanced topic that falls outside the scope of this book.

Relational Operators<utility>

A nice set of relational operators is provided in the std::rel_ops namespace: !=, <=, >, and >=. The first one is implemented in terms of operator==, and the remaining forward to operator<. So, your class only needs to implement operator== and <, and the others are generated automatically when you add a using namespace std::rel_ops;
A417649_1_En_2_Figf_HTML.gif

Smart Pointers<memory>

A smart pointer is an RAII-style object that (typically) decorates and mimics a pointer to heap-allocated memory, while guaranteeing this memory is deallocated at all times once appropriate. As a rule, modern C++ programs should never use raw pointers to manage (co-)owned dynamic memory: all memory allocated by new or new[] should be managed by a smart pointer, or, for the latter, a container such as vector (see Chapter 3). Consequently, C++ programs should rarely directly call delete or delete[] anymore. Doing so will go a long way toward preventing memory leaks.

Exclusive-Ownership Pointers

std::unique_ptr

A unique_ptr has exclusive ownership of a pointer to heap memory and therefore cannot be copied, only moved or swapped. Other than that, it mostly behaves like a regular pointer. The following illustrates its basic usage on the stack:
A417649_1_En_2_Figg_HTML.gif
The -> and * operators ensure that a unique_ptr can generally be used like a raw pointer. Comparison operators ==, !=, <, >, <=, and >= are provided to compare two unique_ptrs or a unique_ptr with nullptr (in either order), but not for comparing a unique_ptr<T> with a T value. To do the latter, get() must be called to access the raw pointer. A unique_ptr also conveniently casts to a Boolean to check for nullptr.
Construction is facilitated using the helper function make_unique(). For example:
{ auto jeff = std::make_unique<Person>("Jeffrey");
  ...
Tip
Using make_unique() not only may shorten your code, but also prevents certain types of memory leaks. Consider f(unique_ptr<X>(new X), g()). If g() throws after the X was constructed, but before it was assigned to its unique_ptr, the X pointer leaks. Writing f(make_unique<X>(), g()) instead guarantees such leaks do not occur.
Other uses of unique_ptrs that make them a truly essential utility include these:
  • They are the safest and recommended way to transfer exclusive ownership, either by returning a unique_ptr from a function that creates a heap object or by passing one as an argument to a function that accepts further ownership. This has three major advantages:
    a.
    In both cases, std::move() generally has to be used, making the ownership transfer explicit.
     
    b.
    The intended ownership transfer also becomes apparent from the functions’ signatures.
     
    c.
    It prevents memory leaks (such bugs can be subtle sometimes: see the next Tip).
     
  • They can be stored safely inside the containers from Chapter 3.
  • When used as member variables of another class, they eliminate the need for explicit deletes in their destructor. Moreover, they prevent the compiler from generating error-prone copy members for objects that are supposed to exclusively own dynamic memory.
A unique_ptr can also manage memory allocated with new[]:
A417649_1_En_2_Figh_HTML.gif
For this template specialization, the dereferencing operators * and -> are replaced with an indexed array access operator []. A more powerful and convenient class to manage dynamic arrays, std::vector, is explained in Chapter 3.
A unique_ptr<T> has two similar members that are often confused: release() and reset(T*=nullptr). The former replaces the old stored pointer (if any) with nullptr, whereas the latter replaces it with the given T*. The key difference is that release() does not delete the old pointer. Instead, release() is intended to release ownership of the stored pointer: it simply sets the stored pointer to nullptr and returns its old value. This is useful to pass ownership to, for example, a legacy API. reset(), on the other hand, is intended to replace the stored pointer with a new value, not necessarily nullptr. Before overwriting the old pointer, it is deleted. It therefore also does not return any value:
A417649_1_En_2_Figi_HTML.gif
Tip
Take care for memory leaks when transferring ownership using release(). Suppose the previous example ended with TakeOwnership(niles.release(), f()). If the call to f() throws after the unique_ptr has released ownership, Niles leaks. Therefore, always make sure expressions containing release() subexpressions do not contain any throwing subexpressions as well. In the example, the solution would be to evaluate f() on an earlier line, storing its result in a named variable. Transferring using std::move(niles), as recommended earlier, would never leak either, by the way. For legacy APIs, though, this is not always an option.
Caution
A fairly common mistake is to use release() where reset() was intended, the latter with the default nullptr argument, ignoring the value returned by release(). The object formerly owned by the unique_ptr then leaks, which often goes unnoticed.
An advanced feature of unique_ptrs is that they can use a custom deleter. The deleter is the functor that is executed when destroying the owned pointer. This is useful for non-default memory allocation, to do additional cleanup, or, for example, to manage a file pointer as returned by the C function fopen() (defined in <cstdio>):
A417649_1_En_2_Figj_HTML.gif
This example uses a deleter of type std::function (defined in the <functional> header, discussed later in this chapter) initialized with a function pointer, but any functor type may be used.

std::auto_ptr

At the time of writing, the <memory> header still defines a second smart pointer type for exclusive ownership, namely std::auto_ptr. This has been deprecated, however, in favor of unique_ptr in C++11, and is set to be removed in C++17. We therefore do not discuss it in detail. Essentially, an auto_ptr is a flawed unique_ptr that is implicitly moved when copied: this makes them not only error-prone but also dangerous (and in fact illegal) to use with the standard containers and algorithms from Chapter 3 and 4.

Shared-Ownership Pointers

std::shared_ptr

When multiple entities share the same heap-allocated object, it is not always obvious or possible to assign a single owner to it. For such cases, shared_ptrs exist, defined in <memory>. These smart pointers maintain a thread-safe reference count for a shared memory resource, which is deleted once its reference count reaches zero: that is, once the last shared_ptr that co-owned it is destructed. The use_count() member returns the reference count, and unique() checks whether the count equals one.
Like a unique_ptr, it has ->, *, cast-to-Boolean, and comparison operators to mimic a raw pointer. Equivalent get() and reset() members are provided as well, but no release(). A shared_ptr cannot manage dynamic arrays, though. What really sets it apart is that shared_ptrs can and are intended to be copied:
A417649_1_En_2_Figk_HTML.gif
A shared_ptr can be constructed by moving a unique_ptr into it, but not the other way around. To construct a new shared_ptr, it is again recommended to use make_shared(): for the same reasons as with make_unique() (shorter code and memory leak prevention), but in this case also because it is more efficient.
Custom deleters are again supported. Unlike with unique_ptr, though, the deleter’s type is not a type argument of the shared_ptr template. The declaration analogous to the one in the earlier example thus becomes:
std::shared_ptr<FILE> smartFilePtr(fopen("test.txt", "r"), fclose);
To obtain a shared_ptr to a related type, use std::static_pointer_cast(), dynamic_pointer_cast(), or const_pointer_cast(). If the result is non-null, the reference count is safely incremented with one. An example will clarify:
A417649_1_En_2_Figl_HTML.gif
A lesser-known feature of shared_ptrs is called aliasing and is used for sharing parts of an already shared object. It is best introduced with an example:
A417649_1_En_2_Figm_HTML.gif
A shared_ptr has both an owned pointer and a stored pointer. The former determines the reference counting, and the latter is returned by get(), *, and ->. Generally both are the same, but not if constructed with the aliasing constructor. Almost all operations use the stored pointer, including the comparison operators <, >=, and so on. To compare based on the owned rather than the stored pointer, use the owner_before() member or std::owner_less<> functor class (functors are explained shortly). This is useful, for example, when storing shared_ptrs in a std::set (see Chapter 3).

std::weak_ptr

There are times, particularly when building caches of shared objects, when you want to keep a reference to a shared object should you need it, but you do not want your reference to necessarily prevent the deletion of the object. This concept is commonly called a weak reference and is offered by <memory> in the form of a std::weak_ptr.
A non-empty weak_ptr is constructed with a shared_ptr or results from assigning a shared_ptr to it afterward. These pointers can again be freely copied, moved, or swapped. Although a weak_ptr does not co-own the resource, it can access its use_count(). To check whether the shared resource still exists, expired() can be used as well (it is equivalent to use_count()==0). The weak_ptr, however, does not have direct access to the shared raw pointer, because nothing would then prevent the last co-owner from concurrently deleting it. To access the resource, a weak_ptr therefore first has to be promoted to a co-owning shared_ptr using its lock() member:
A417649_1_En_2_Fign_HTML.gif

Function Objects<functional>

A function object or functor is an object with an operator()(T1,...,Tn) (n may be zero), allowing it to be invoked just like a function or operator:
A417649_1_En_2_Figo_HTML.gif
Functors not only can be passed to many standard algorithms (Chapter 4) and concurrency constructs (Chapter 7), but are also very useful for creating your own generic algorithms or, for example, storing or providing callback functions.
This section outlines the functors defined in <functional>, as well as its facilities for creating and working with functors.1 We also briefly introduce lambda expressions, the powerful C++11 language construct for creating functors.
Before we delve into functors, though, a short word on the reference wrapper utilities that are defined in the <functional> header.

Reference Wrappers

The functions std::ref() and cref() return std::reference_wrapper<T> instances that simply wrap a (const) T& reference to their input argument. This reference can then be extracted explicitly with get() or implicitly by casting to T&.
Because these wrappers can safely be copied, they can be used, for example, to pass references to template functions that take their arguments by value, badly forward their arguments (forwarding is discussed earlier in this chapter), or copy their arguments for other reasons. Standard template functions that do not accept references as arguments, but do work with ref()/cref(), include std::thread() and async() (see Chapter 7), and the std::bind() function discussed shortly.
These wrappers can be assigned to as well, thus enabling storing references into the containers from Chapter 3. In the following example, for instance, you could not declare a vector<int&>, because int& values cannot be assigned to:
A417649_1_En_2_Figp_HTML.gif

Predefined Functors

The <functional> header provides an entire series of functor structs similar to the my_plus example used earlier in this section’s introduction:
  • plus, minus, multiplies, divides, modulus, and negate
  • equal_to, not_equal_to, greater, less, greater_equal, and less_equal
  • logical_and, logical_or, and logical_not
  • bit_and, bit_or, bit_xor, and bit_not
These functors often result in short, readable code, even more so than with lambda expressions. The following example sorts an array in descending order (with the default being ascending) using the sort() algorithm explained in Chapter 4:
int array[] = { 7, 9, 7, 2, 0, 4 };
std::sort(begin(array), end(array), std::greater<int>());
As of C++14, all of these functor classes have a special specialization for T equal to void, and void has also become the default template type argument. These are called transparent operator functors , because their function call operator conveniently deduces the parameter type. In the previous sort() example, for instance, you could simply use std::greater<>. The same functor can even be used for different types:
A417649_1_En_2_Figq_HTML.gif
As Chapter 3 explains, the transparent std::less<> and greater<> functors are also the preferred comparison functors for ordered associative containers.
Passing a unary/binary functor, predicate, to std::not1()/not2() creates a new functor (of type unary_negate/binary_negate) that negates predicate’s result (that is, evaluates to !predicate()). For this to work, the type of predicate must define a public member type argument_type. All functor types in <functional> have this.

Generic Function Wrappers

The std::function template class is designed for wrapping a copy of any kind of callable entity: that is, any kind of function object or pointer. This includes the results of, for example, bind or lambda expressions (both explained in more detail shortly):
A417649_1_En_2_Figr_HTML.gif
If a default-constructed function object is called, a std::bad_function_call exception is thrown. To verify whether a function may be called, it conveniently casts to a Boolean. Alternatively, you may compare a function to a nullptr using == or !=, just as you would with a function pointer.
Other members include target<Type>() to obtain a pointer to the wrapped entity (the correct Type must be specified; otherwise the member returns nullptr), and target_type() which returns the type_info for this wrapped entity (type_info is explained under “Type Utilities” later in this chapter).
Tip
A lesser-known feature of std::ref(), cref(), and their return type reference_wrapper, seen earlier, is that they can also be used to wrap callables. Unlike a std::function, though, which stores a copy of the callable, a reference_wrapper stores a reference to it. This is useful when passing a functor you do not want to be copied—for example, because it is too large (performance), stateful, or simply uncopyable—to an algorithm that accepts it or may pass it around by value. For example:
function_that_copies_its_callable_argument(std::ref(my_functor));
Note that for the standard algorithms from Chapter 4, it is generally unspecified how often they copy their arguments. So to guarantee no copies are made, you must use (c)ref().

Binding Function Arguments

The std::bind() function may be used to wrap a copy of any callable while changing its signature: parameters may be reordered, assigned fixed values, and so on. To specify which arguments to forward to the wrapped callable, a sequence of either values or so-called placeholders (_1, _2, and so forth) is passed to bind(). The first argument passed to the bound functor is forwarded to all occurrences of placeholder _1, the second to those of _2, and so on. The maximum number of placeholders is implementation specific; and the type of the returned functors is unspecified. Some examples will clarify:
A417649_1_En_2_Figs_HTML.gif

Functors for Class Members

Both std::function and bind(), introduced earlier, may be used to create functors that evaluate to a given object’s member variable or that call a member function on a given object. A third option is to use std::mem_fn(), which is intended specifically for this purpose:
struct my_struct { int val; bool fun(int i) { return val == i; } };
int main() {
   my_struct s{234};
   std::function<int(my_struct&)> f_get_val = &my_struct::val;
   std::function<bool(my_struct&,int)> f_call_fun = &my_struct::fun;
   std::cout << f_get_val(s) << ' ' << f_call_fun(s, 123) << std::endl;
   using std::placeholders::_1;
   auto b_get_val = std::bind(&my_struct::val, _1);
   auto b_call_fun_on_s = std::bind(&my_struct::fun, std::ref(s), _1);
   std::cout << b_get_val(s) << ' ' << b_call_fun_on_s(234) << std::endl;
   auto m_get_val = std::mem_fn(&my_struct::val);
   auto m_call_fun = std::mem_fn(&my_struct::fun);
   std::cout << m_get_val(s) << ' ' << m_call_fun(s, 456) << std::endl;
}
The member functors created by bind() and mem_fn(), but not std::functions, may also be called with a pointer or one of the standard smart pointers (see the previous section) as the first argument (that is, without dereferencing). Interesting also about the bind() option is that it can bind the target object itself (see b_call_fun_on_s). If that is not required, std::mem_fn() generally results in the shortest code because it deduces the entire type. A more realistic example is this (vector, count_if(), and string are explained in Chapters 3, 4, and 6, respectively):
A417649_1_En_2_Figt_HTML.gif
LAMBDA EXPRESSIONS
Although not part of the Standard Library, lambda expressions are such a powerful tool for creating functors that they are well worth a short introduction. In particular, when combined with the algorithms from Chapter 4, the concurrency constructs from Chapter 7, and so on, they often form the basis of very expressive, elegant code. Several examples of lambda expressions can be found throughout this book, especially in Chapter 4.
A lambda expression is often said to create an anonymous function, but actually it creates a functor of unspecified type, also called a closure. A lambda expression does not have to start from an existing function like the <functional> constructs: the body of its closure’s function call operator may contain arbitrary code.
The basic syntax of a lambda expression is as follows:
[CaptureBlock](Parameters) mutable -> ReturnType {Body}
Capture block: Specifies which variables from the enclosing scope to capture. Essentially, the created functor has, for each captured variable, a member with the same name containing a copy of this captured variable if it is captured by value, or a reference to it if captured by reference. As such, these variables become available for use in the body. The basic syntax of a capture block:
  • [] captures no variables (cannot be omitted).
  • [x, &y] captures x by value and y by reference.
  • [=, &x] captures all variables from the enclosing scope by value except x, which is captured by reference.
  • [&, x,y] captures all variables by reference except x and y, which are captured by value.
  • [this] captures the this pointer, granting the body access to all members of the surrounding object.
Parameters: Parameters to be passed when calling the functor. Omitting is equivalent to specifying an empty list (). The type of parameters may be auto.
mutable: By default, the function call operator of a lambda functor is always marked as const, implying that variables captured by value—that is, copied in member variables—cannot be modified (assigned to, non-const members called, and so on). Specifying mutable makes the function call operator non-const.
Return type: The type of the returned value. May be omitted as long as all return statements of the body return the exact same type.
Body: The code to execute when the lambda functor is called (non-optional).
It is also possible to specify noexcept and/or attributes (after the optional mutable), but these are rarely used.

Initializer Lists<initializer_list>

The initializer_list<T> type is used by the C++ compiler to represent the result of initializer-list declarations:
A417649_1_En_2_Figu_HTML.gif
This curly-braces syntax is the only way to create non-empty initializer lists. Once created, initializer_lists are immutable. Their few operations, size(), begin(), and end(), are analogous to those of containers (Chapter 3). When constructing an initializer_list from a list of initialization values, the list stores a copy of those values. However, copying an initializer_list does not copy the elements: the new copy simply refers to the same array of values.
The single most common use case for initializer_lists is probably initializer-list constructors , which are special in the sense that they take precedence over any other constructors when curly braces are used:
A417649_1_En_2_Figv_HTML.gif
All container classes from Chapter 3, for instance, have initializer-list constructors to initialize them with a list of values.

Date and Time Utilities<chrono>

The <chrono> library introduces utilities mainly for tracking time and durations at varying degrees of precision, determined by the type of clock used. To work with dates, you have to use the C-style date and time types and functions defined in <ctime>. The system_clock from <chrono> allows for interoperability with <ctime>.

Durations

A std::chrono::duration<Rep, Period=std::ratio<1>> expresses a time span as a tick count, represented as a Rep value which is obtainable through count() (Rep is or emulates an arithmetic type). The time between two consecutive ticks, or period, is statically determined by Period, a std::ratio type denoting a number (or fraction) of seconds (std::ratio is explained in Chapter 1). The default Period is one second:
A417649_1_En_2_Figw_HTML.gif
The duration constructor can convert between durations of a different Period and/or count Representation, as long as no truncation is required. The duration_cast() function can be used for truncating conversions as well:
A417649_1_En_2_Figx_HTML.gif
For convenience, several typedefs analogous to those in the previous example are predefined in the std::chrono namespace: hours, minutes, seconds, milliseconds, microseconds, and nanoseconds. Each uses an unspecified signed integral Rep type, at least big enough to represent a duration of about 1,000 years (Rep has at least 23, 29, 35, 45, 55, and 64 bits, respectively). For further convenience, the namespace std::literals::chrono_literals contains literal operators to easily create instances of such duration types: h, min, s, ms, us, and ns, respectively. They are also made available with a using namespace std::chrono declaration. When applied on a floating-point literal, the result has an unspecified floating point type as Rep:
A417649_1_En_2_Figy_HTML.gif
All arithmetic and comparison operators that you would intuitively expect for working with durations are supported: +, -, *, /, %, +=, -=, *=, /=, %=, ++, --, ==, !=, <, >, <=, and >=. The following expression, for example, evaluates to a duration with count() == 22:
duration_cast<minutes>((12min + .5h) / 2 + (100ns >= 1ms? -3h : ++59s))

Time Points

A std::chrono::time_point<Clock, Duration=Clock::duration> represents a point in time, expressed as a Duration since a Clock’s epoch. This Duration may be obtained from its time_since_epoch() member. The epoch is defined as the instant in time chosen as the origin for a particular clock, the reference point from which time is measured. The available standard Clocks are introduced in the next section.
A time_point is generally originally obtained from a member of its Clock’s class. It may be constructed from a given Duration as well, though. If default-constructed, it represents the Clock’s epoch. Several arithmetic (+, -, +=, -=) and comparison (==, !=, <, >, <=, >=) are again available. Subtracting two time_points results in a Duration, and Durations may be added to and subtracted from a time_point. Adding time_points together is not allowed, nor is subtracting one from a Duration:
A417649_1_En_2_Figz_HTML.gif
Conversion between time_points with different Duration types works analogously to the conversion of durations: implicit conversions is allowed as long as no truncation is required; otherwise, time_point_cast() can be used:
auto one_hour = time_point_cast<hours>(sixty_minutes);

Clocks

The std::chrono namespace offers three clock types: steady_clock, system_clock, and high_resolution_clock. All clocks define the following static members:
  • now(): A function returning the current point in time.
  • rep, period, duration, time_point: Implementation-specific types. time_point is the type returned by now(): an instantiation of std::chrono::time_point with Duration type argument equal to duration, which in turn equals std::chrono::duration<rep, period>.
  • is_steady: A Boolean constant that is true if the time between clock ticks is constant and two consecutive calls to now() always return time_points t1 and t2 for which t1 <= t2.
The only clock that is guaranteed to be steady is steady_clock. That is, this clock cannot be adjusted. The system_clock, on the other hand, corresponds to the system-wide real-time clock, which can generally be set at will by the user. The high_resolution_clock, finally, is the clock with the shortest period supported by the library implementation (it may be an alias for steady_clock or system_clock).
To measure the time an operation took, a steady_clock should therefore be used, unless the high_resolution_clock of your implementation is steady:
A417649_1_En_2_Figaa_HTML.gif
The system_clock should be reserved for working with calendar time. Because the facilities of <chrono> in that respect are somewhat limited, this clock offers static functions to convert its time_points to time_t objects and vice versa (to_time_t() and from_time_t() respectively), which can then be used with the C-style date and time utilities discussed in the next subsection:
A417649_1_En_2_Figab_HTML.gif

C-style Date and Time Utilities<ctime>

The <ctime> header defines two interchangeable types to represent a date and time: time_t, an alias for an arithmetic type (generally a 64-bit signed integer), represents time in a platform-specific manner; and tm, a portable struct with these fields: tm_sec (range [0,60], where 60 is used for leap seconds), tm_min, tm_hour, tm_mday (day of the month, range [1,31]), tm_mon (range [0,11]), tm_year (year since 1900), tm_wday (range [0,6], with 0 being Sunday), tm_yday (range [0,365]), and tm_isdst (positive if Daylight Saving Time is in effect, zero if not, and negative if unknown).
The following functions are available with <ctime>. The local time zone is determined by the currently active C locale (locales are explained in Chapter 6):
Function
Returns
clock()
A clock_t (an arithmetic type) with the approximate processor time consumed by the process in clock ticks; or -1 upon failure. The clock’s period is stored in the CLOCKS_PER_SEC constant. Although this clock is steady, it may run at a different pace than wall clock time (slowed down due to context switches, sped up due to multithreading, and so on).
time()
Current point in time as a time_t, or null on failure. A time_t* argument must be passed: if not null, value is written there as well.
difftime()
The difference between two time_ts as a double value denoting a time in seconds (result may be negative).
mktime()
A time_t, converted from a tm* for the local time zone, or -1 on failure.
localtime()
gmtime()
A pointer to a statically allocated tm to which the conversion for the local / GMT time zone from a given time_t* has been written, or null on failure. These functions are not thread-safe: this global tm is possibly shared among localtime(), gmtime(), and ctime().
asctime()
ctime()
A char* pointer into a global buffer in which the (null-terminated) textual representation of a given tm* or, respectively, time_t* is written, using a fixed, locale-independent format. They are thus both limited and not thread-safe, so they have been deprecated in favor of, for example, strftime() .
strftime()
Explained next.
Consult your implementation’s documentation for safer alternatives for localtime() and gmtime() (such as localtime_s() for Windows or localtime_r() for Linux). For converting dates and times to strings, the preferred C-style function is strftime() (at the end of this section, we point out C++-style alternatives):
size_t strftime(char* result, size_t n, const char* format, const tm*);
An equivalent for converting to wide strings (wchar_t sequences), wcsftime(), is defined in <cwchar>. These functions write a null-terminated character sequence into result, which must point to a preallocated buffer of size n. If this buffer is too small, zero is returned. Otherwise, the return value equals the number of characters written, not including the terminating null character.
The grammar for specifying the desired textual representation is defined as follows: any character in the format string is copied to the result, except certain special specifiers that are replaced as shown in the following table:
Specifier
Output
Range or Example
%M / %S
Minutes / seconds.
[00,59] / [00,60]
%H / %I
Hours using 24h / 12h clock.
[00,23] / [01,12]
%R / %T
Equivalent to "%H:%M" / "%H:%M:%S".
04:29 / 04:29:00
%p / %r
A.m. or p.m. / full 12h clock time.
pm / 04:29:00 pm
%A / %a
Full / abbreviated weekday name.
Wednesday / Wed
%u / %w
Weekday number, where the first number in the range stands for Monday / Sunday.
[1-7] / [0-6]
%d / %e
Day of the month.
[01-31] / [1-31]
%j
Day of the year.
[001-366]
%U / %V / %W
Week of the year, with weeks starting at Sunday (%U) or Monday (%V, %W); %V determines the first week of the year according to ISO 8601.
[00,53]    (%U,%W)
/ [01,53]  (%V)
%B / %b, %h
Full / abbreviated month name (%h is an alias for %b) .
October / Oct
%m
Month number.
[01-12]
%Y / %G
Year / year current week belongs to, per ISO 8601.
2015
%C / %y / %g
First (%C) / last (%y, %g) two digits of the year. %g uses the year the current week belongs to, per ISO 8601.
20 / 15 / 15
%D / %F
Equivalent to "%m/%d/%y" / "%Y-%m-%d".
10/21/15 /
2015-10-21
%c / %x / %X
Preferred date + time / date / time representation.
(see below)
%Z / %z
If available (empty if not): time zone name or abbreviation / offset from UTC as "±hhmm".
PDT / -0700
%% / %t / %n
Escaping / special characters.
% / /
A417649_1_En_2_Figac_HTML.gif
The result of many specifiers, including those that expand to names or preferred formats, depends on the active locale (see Chapter 6). When executed with a French locale, for example, the output for the previous example could be “Today is mer. 21/11" and "10/21/15 16:29:00--10/21/15 16:29:00”. To use a locale-dependent alternative representation (if one is defined by the current locale), C, c, X, x, Y, and y may be preceded by an E (%EC, %Ec, and so on); to use alternative numeric symbols, d, e, H, I, M, m, S, u, U, V, W, w, and y may be modified with the letter O.
As covered in Chapter 5, the C++ libraries offer facilities for reading/writing a tm from/to a stream as well, namely get_time() and put_time(). The only C-style function from <ctime> you generally need to output calendar dates and time in C++-style is therefore localtime() (to convert a system_clock’s time_t to tm).

C-Style File Utilities<cstdio>

The next version of the C++ Standard Library is expected to include a more powerful C++-style file system library. For now, this limited set of C-style functions in the <cstdio> header are the only portable file utilities available in the Standard:
Function
Description
int remove(filename)
Deletes the file with the given filename. Returns 0 on success. It is implementation dependent whether errno (see Chapter 8) is set when there is an error.
int rename(old, new)
The file named old is renamed to new. If supported, files may be moved to a different path as well. Returns 0 on success. It is implementation dependent whether errno (see Chapter 8) is set when there is an error.
FILE* tmpfile()
Opens a newly created file with a generated unique name for binary output. The returned FILE* pointer can be used with the C-style I/O functions briefly discussed in Chapter 5. The temporary file is automatically deleted when it is closed. Returns nullptr when the file could not be created.
char* tmpnam(char*)
Creates a unique, non-existing filename. If a char* argument is provided, the result is stored in this pointer and the pointer is returned as well. The provided char* buffer must be at least L_tmpnam bytes long. If the argument is nullptr, a pointer to an internal static buffer is returned in which the filename is put. Returns nullptr if no filename could be generated.

Type Utilities

Runtime Type Identification <typeinfo>, <typeindex>

The C++ typeid() operator is used to obtain information about the runtime type of a value. It returns a reference to a global instance of the std::type_info class defined in <typeinfo>. These instances cannot be copied, but it is safe to use references or pointers to them. Comparison is possible using their ==, !=, and before() members, and a hash_code() can be computed for them. Of particular interest is name(), which returns an implementation-specific textual representation of the value’s type:
A417649_1_En_2_Figad_HTML.gif
The name() printed may be something like “std::basic_string<char, std::char_traits<char>, std::allocator<char>>” (see Chapter 6), but for other implementations it might just as well be “Ss”.
When used on a B* pointer to an instance of a derived class D, typeid() only gives the dynamic type D* rather than the static type B* if B is polymorphic—that is, has at least one virtual member.
Because type_infos cannot be copied, they cannot be used as keys for the associative arrays from Chapter 3 directly. For precisely this purpose, the <typeindex> header defines the std::type_index decorator class: it mimics the interface of a wrapped type_info&, but it is copyable; has <, <=, >, and >= operators; and has a specialization of std::hash defined for it.

Type Traits <type_traits>

A type trait is a construct used to obtain compile-time information on a given type or to transform a given type or types to a related one. Type traits are generally used to inspect and manipulate template type arguments when writing generic code.
The <type_traits> header defines a multitude of traits. Due to page constraints, and because template metaprogramming is an advanced topic, this book cannot go into details on all of them. We provide a brief reference on the different type traits, though, which should be sufficient for basic usage.

Type Classification

Each type in C++ belongs to exactly one of 14 primary type categories. In addition to those, the Standard also defines several composite type categories to easily refer to all types belonging to two or more related primary categories. For each of these, a type trait struct exists to check whether a given type belongs to that category. Their names are of the form is_ category, with category equal to one of the names shown in Figure 2-1. A trait’s static Boolean named value contains whether its type argument belongs to the corresponding category. Traits are functors that both return and cast to this value. Some examples follow (the code refers to int main()):
A417649_1_En_2_Fig1_HTML.jpg
Figure 2-1.
Overview of the type classification traits. The second column lists the 14 primary categories; the other names are those of the composite categories.
A417649_1_En_2_Figae_HTML.gif

Type Properties

A second series of type traits is there to statically query properties of types. They are mostly used in exactly the same manner as those of the previous subsection, and all except one, has_virtual_destructor, again have names of the form is_ property.
The following values for property check the indicated type properties:
  • The presence of type quantifiers: const and volatile
  • Polymorphism properties of classes: polymorphic (has virtual member(s)), abstract (pure virtual member(s)), and final
  • Signedness of arithmetic types: signed (includes floating-point numbers) and unsigned (includes Booleans)
And then there is a large family of traits where the property is the validity of a construction or assignment statement with specified argument types, or the validity of a destruction statement (omitting as always the is_):
  • The basic ones are constructible<T,Args...>, assignable<T, Arg>, and destructible<T>. All scalar types are destructible, and the former two properties may hold for non-class types as well (because constructions like int i(0);, for example, are valid).
  • Auxiliary traits exist for checking the validity of default constructions (default_constructible) and copy/move constructions and assignments (copy_constructible<T> == constructible<T, const T&>, and so on).
  • All the previous property names may further be prefixed with either trivially or nothrow. For instance: trivially_destructible, nothrow_constructible, or nothrow_move_assignable.
The nothrow properties hold if the construction, assignment, or destruction is statically known to never throw. The trivial ones hold if the type is either scalar or a non-polymorphic class for which this operation is the default one (that is, not specified by the user), and the trivial property holds as well for all its base classes and non-static member variables. For the trivially constructible properties, the class is also not allowed to have any non-static data members with in-class initializers.
The final list of property values essentially hold under the following conditions. Arrays of types satisfying these also have the same property:
  • trivially_copyable, if trivially_destructible and trivially_(copy|move)_(constructible|assignable) all hold. Bitwise copy functions such as std::memcpy() are defined to be safe for trivially_copyable types.
  • trivial, if trivially_default_constructible and trivially_copyable, and no non-default constructors exist.
  • standard_layout, if scalar or a class for which a pointer to that class may safely be casted to a pointer to the type of its first non-static member (that is, no polymorphism, limited multiple-inheritance, and so on). This is for compatibility with C, because such casts (with C structs then) are common practice in C code.
  • pod (plain old data), if trivial and standard_layout.
  • literal_type, if values may be used in constexpr expressions (that is, can be evaluated statically without side effects).
  • empty, for non-polymorphic classes without non-static member variables.

Type Property Queries

The value of a type trait is not always a Boolean. For the following traits, it contains the specified size_t type properties:
  • std::alignment_of<T>: value of the alignof(T) operator
  • std::rank<T>: array dimensions, such as rank<int>() == 0, rank<int[]>() == 1, rank<int[][5][6]>() == 3, and so on
  • std::extent<T,N=0>: number of elements of the Nth array dimension, or 0 if unknown or invalid; for instance, extent<int[]>() == 0 and extent<int[][5][6], 1>() == 5.

Type Comparisons

These three type traits compare types: is_same<T1, T2>, is_base_of<Base, Derived>, and is_convertible<From, To> (using implicit conversions).

Type Transformations

Most type transformation traits are again fairly similar, except that they have no value, but instead a nested typedef named type:
  • std::add_x with x one of const, volatile, cv (const and volatile), pointer, lvalue_reference, rvalue_reference.
  • std::remove_x with x one of const, volatile, cv, pointer, reference (lvalue or rvalue), extent, all_extents. In all except the last case, only the top-level/first type modifier is removed. For instance: remove_extent<int[][5]>::type == int[5].
  • std::decay<T>: converts T to a related type that can be stored by value, mimicking by-value argument passing. An array type int[5], for example, becomes a pointer type int*, a function a function pointer, const and volatile are stripped, and so on. A possible implementation is shown shortly as an example.
  • std::make_y with y either signed or unsigned. If applied on an integral type T, type is a signed or, respectively, unsigned integer type with sizeof(type) == sizeof(T).
  • std::result_of, defined only for functional types, gives the return type of the function.
  • std::underlying_type, defined only for enum types, gives the (integral) type underlying this enum.
  • std::common_type<T...> has a type all types T can implicitly be converted to.
The header also contains two utility traits to help with type metaprogramming. Their basic use is illustrated by means of a few examples.
  • std::conditional<B,T1,T2> has type T1 if the constexpr B evaluates to true and type T2 otherwise.
  • std::enable_if<B,T=void> has type T, but only if the constexpr B evaluates to true. Otherwise, type is not defined.
For all traits of this subsection, a convenience type with name std:: trait _t<T> exists defined as std:: trait <T>::type. The upcoming example, for instance, shows how convenient enable_if_t<> is compared to the full expression.
This first example shows how to use the C++ SFINAE idiom to conditionally add or remove functions from overload resolution. SFINAE is an acronym for Substitution Failure Is Not An Error and exploits the fact that failure to specialize a template does not constitute a compile error. In this case, it is the absence of the type typedef that causes substitution to fail:
A417649_1_En_2_Figaf_HTML.gif
A second example shows a possible implementation of the std::decay transformation trait in terms of the std::conditional metafunction. The latter is used to essentially form an if-else if-else construction at the level of types:
using namespace std;
template<typename T> struct my_decay {
private:
   typedef remove_reference_t<T> U;
public:
   typedef conditional_t<is_array<U>::value, remove_extent_t<U>*,
           conditional_t<is_function<U>::value, add_pointer_t<U>,
           remove_cv_t<U>>> type;
};
Footnotes
1
<functional> contains many deprecated facilities we do not discuss: ptr_fun(), mem_fun(), mem_fun_ref(), bind1st(), and bind2nd(), plus their return types, as well as the base classes unary_function and binary_function. All of these have already been removed from the C++17 version of the standard and should not be used.
 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset