A tour of c++ (2nd edition) (c++ in depth series) In A Tour of C++, Second Edition, Bjarne Stroustrup, the creator of C++, describes what constitutes modern C++. This concise, selfcontained guide covers most major language features and the major standardlibrary components―not, of course, in great depth, but to a level that gives programmers a meaningful overview of the language, some key examples, and practical help in getting started. Stroustrup presents the C++ features in the context of the programming styles they support, such as objectoriented and generic programming. His tour is remarkably comprehensive. Coverage begins with the basics, then ranges widely through more advanced topics, including many that are new in C++17, such as move semantics, uniform initialization, lambda expressions, improved containers, random numbers, and concurrency. The tour even covers some extensions being made for C++20, such as concepts and modules, and ends with a discussion of the design and evolution of C++. This guide does not aim to teach you how to program (for that, see Stroustrup’s Programming: Principles and Practice Using C++, Second Edition), nor will it be the only resource you’ll need for C++ mastery (for that, see Stroustrup’s The C++ Programming Language, Fourth Edition, and recommended online sources). If, however, you are a C or C++ programmer wanting greater familiarity with the current C++ language, or a programmer versed in another language wishing to gain an accurate picture of the nature and benefits of modern C++, you can’t find a shorter or simpler introduction than this tour provides.
Introduction
This chapter introduces the fundamental concepts of C++, including its notation, memory model, and computational structure It also outlines the essential mechanisms for structuring code within a program, highlighting the language features that facilitate procedural programming, a style commonly associated with C.
Programs
C++ is a compiled programming language that requires its source code to be processed by a compiler, generating object files These object files are then linked together to create an executable program Typically, a C++ program is made up of multiple source files, which are essential for the compilation and linking process to produce the final executable.
An executable program is designed for a particular hardware or system, making it non-portable between different platforms, such as from Mac to Windows In the context of C++ programming, portability typically refers to the source code, which should be able to compile and run successfully across multiple systems.
The ISO C++ standard defines two kinds of entities:
• Core language features, such as built-in types (e.g., char and int ) and loops (e.g., for -state- ments and while -statements)
• Standard-library components, such as containers (e.g., vector and map ) and I/O operations (e.g., act; // read characters into a string
Point delta {0,0}; // Point holds an {x,y} pair for (char ch : act) { switch (ch) { case 'u': // up case 'n': // nor th
++delta.y; break; case 'r': // right case 'e': // east
// more actions default: cout >v.elem[i]; // read into elements
Section 2.2 Structures 23 double sum = 0; for (int i=0; i!=s; ++i) sum+=v.elem[i]; // compute the sum of the elements return sum;
There is a long way to go before our Vector is as elegant and flexible as the standard-library vector
Understanding the intricacies of Vector's representation is essential for users This chapter, along with the next two, will progressively enhance the understanding of Vector through various language features and techniques Additionally, Chapter 11 introduces the standard-library vector, showcasing numerous valuable improvements.
I use vector and other standard-library components as examples
• to illustrate language features and design techniques, and
• to help you learn and use the standard-library components.
Don’t reinvent standard-library components such as vector and string ; use them.
We use (dot) to access struct members through a name (and through a reference) and −> to access struct members through a pointer For example: void f(Vector v, Vector& rv, Vector ∗ pv)
{ int i1 = v.sz; // access through name int i2 = rv.sz; // access through reference int i3 = pv−>sz; // access through pointer
Classes
Separating data from its operations offers flexibility in how the data can be utilized, but for user-defined types to function like "real types," a stronger connection between representation and operations is essential This often involves restricting access to the underlying representation, which simplifies usage, ensures consistent data handling, and allows for future enhancements To achieve this, it is crucial to differentiate between a type's interface, accessible to all users, and its implementation, which can access the private data This concept is encapsulated in the language mechanism known as a class, which comprises members that can be data, functions, or types The public members of a class define its interface, while private members can only be accessed through that interface.
The Vector class is designed to manage a dynamic array of double elements, initialized with a specified size It features a constructor that allocates memory for the elements and a subscripting operator for element access Additionally, the class includes a method to retrieve the current size of the array, while maintaining private member variables for the pointer to the elements and the total count of elements.
Given that, we can define a variable of our new type Vector :
We can illustrate a Vector object graphically:
The Vector object acts as a "handle" that points to its elements (elem) and keeps track of the number of elements (sz) In this example, the number of elements can change, demonstrating the dynamic nature of the Vector object.
A Vector object in C++ can hold a varying number of elements over time, while maintaining a constant size for the object itself This approach allows for efficient management of dynamic data by using a fixed-size handle that references a variable amount of information stored elsewhere, such as in the free store allocated by the new operator The design and utilization of such objects are explored in detail in Chapter 4.
The representation of a Vector, including its elements (elem) and size (sz), is only accessible through the public interface provided by the members: Vector(), operator[](), and size() In the example from §2.2, the read_and_sum() function is simplified to: double read_and_sum(int s).
Vector v(s); // make a vector of s elements for (int i=0; i!=v.siz e(); ++i) cin>>v[i]; // read into elements double sum = 0; for (int i=0; i!=v.siz e(); ++i) sum+=v[i]; // take the sum of the elements return sum;
A constructor is a special member function that shares its name with the class, specifically designed to create and initialize objects of that class In this context, the constructor Vector() replaces the previous function vector_init() from §2.2 Unlike standard functions, a constructor is inherently used for initializing class objects, effectively addressing the issue of uninitialized variables within a class.
The Vector(int) constructor is responsible for creating Vector objects by requiring an integer input, which specifies the number of elements in the Vector It initializes the Vector's members through a member initializer list.
That is, we first initialize elem with a pointer to s elements of type double obtained from the free store Then, we initialize sz to s
Access to elements is provided by a subscript function, called operator[] It returns a reference to the appropriate element (a double& allowing both reading and writing).
The siz e() function is supplied to give users the number of elements.
Error handling is currently absent, which will be addressed in §3.5 Additionally, there is no mechanism to return the array of doubles allocated with new; §4.2.2 illustrates how to effectively use a destructor for this purpose.
A struct and a class in programming are fundamentally the same, with the key distinction being that members of a struct are public by default, while members of a class are private Additionally, you can define constructors and member functions for a struct, just as you would for a class.
Unions
A union is a data structure where all members share the same memory address, allowing it to occupy space equivalent to its largest member This means a union can only store a value for one member at any given time For instance, in a symbol table entry that includes a name and a value, the value can be either a pointer to a Node or an integer, as defined by an enumeration of types The struct Entry consists of a string for the name, utilizing a standard library type.
Node ∗ p; // use p if t==ptr int i; // use i if t==num
{ if (pe−>t == num) cout i;
To optimize space efficiency, the members 'p' and 'i' should not be used simultaneously; instead, they can be defined as part of a union By declaring them within a union structure, such as `union Value {`, memory usage can be effectively minimized.
The language doesn’t keep track of which kind of value is held by a union , so the programmer must do that: struct Entry { string name;
Value v; // use v.p if t==ptr; use v.i if t==num
{ if (pe−>t == num) cout v.i;
To minimize errors in maintaining the correspondence between a type field and the type held in a union, it is effective to encapsulate both the union and the type field within a class By providing access exclusively through member functions that correctly utilize the union, we can ensure accurate type handling and enhance the reliability of the code.
At the application level, abstractions relying on suchtagged unions are common and useful The use of ‘‘naked’’ union s is best minimized.
The standard library type, variant, effectively replaces most direct uses of unions by storing a value from a specified set of alternative types For instance, a variant can accommodate either a Node pointer or an integer, enhancing type safety and flexibility in programming.
Using variant , the Entr y example could be written as: struct Entry { string name; variant v;
{ if (holds_alternative(pe−>v)) // does *pe hold an int? (see §13.5.1) cout v); // get the int
For many uses, a variant is simpler and safer to use than a union
Enumerations
C++ provides a straightforward way to create user-defined types through enumerations, allowing developers to define a set of named values For example, an enumeration can represent colors with the declaration `enum class Color { red, blue, green };`, while another can define traffic light states with `enum class Traffic_light { green, yellow, red };` This feature enhances code readability and type safety in C++ programming.
Traffic_light light = Traffic_light::red;
Enumerators, such as Color::red, are confined within their respective enum classes, allowing for their repeated use across different enum classes without ambiguity For instance, Color::red refers specifically to the red enumerator within the Color class, distinguishing it from Traffic_light::red, which belongs to the Traffic_light class.
Enumerations enhance code readability and reduce errors by representing small sets of integer values with symbolic and mnemonic names.
An enum class enforces strong typing and scoping for enumerations, ensuring that enumerators are distinct types This design prevents the accidental mixing of constants, such as Traffic_light and Color values, enhancing code safety and clarity.
Color x = red; // error : which red?
Color y = Traffic_light::red; // error : that red is not a Color
Similarly, we cannot implicitly mix Color and integer values: int i = Color::red; // error : Color ::red is not an int
Color c = 2; // initialization error: 2 is not a Color
Catching attempted conversions to an enum helps prevent errors, but we often need to initialize an enum with a value from its underlying type, typically int This initialization is permitted, along with explicit conversions from the underlying type.
Color x = Color{5}; // OK, but verbose
By default, an enum class has only assignment, initialization, and comparisons (e.g., == and < ; §1.4) defined However, an enumeration is a user-defined type, so we can define operators for it:
Traffic_light& operator++(Traffic_light& t) // prefix increment: ++
{ switch (t) { case Traffic_light::green: return t=Traffic_light::yellow; case Traffic_light::yellow: return t=Traffic_light::red; case Traffic_light::red: return t=Traffic_light::green;
Traffic_light next = ++light; // next becomes Traffic_light::green
To avoid explicitly qualifying enumerator names and to ensure that enumerator values are treated as integers without requiring explicit conversion, simply remove the class designation from the enum class.
A "plain" enum defines enumerators that exist within the same scope as the enum's name, automatically converting to their corresponding integer values For instance, in the declaration `enum Color { red, green, blue };`, the enumerator `green` can be assigned to an integer variable like so: `int col = green;`.
In C++, enumerator values begin at 0 and increment by one for each subsequent enumerator, with the default value for 'col' set to 1 Although "plain" enums have been part of C and C++ since their inception, they remain prevalent in modern code despite being less strictly defined.
Advice
[1] Prefer well-defined user-defined types over built-in types when the built-in types are too low- level; §2.1.
[2] Organize related data into structures ( struct s or class es); §2.2; [CG: C.1].
[3] Represent the distinction between an interface and an implementation using a class ; §2.3; [CG: C.3].
[4] A struct is simply a class with its members public by default; §2.3.
[5] Define constructors to guarantee and simplify initialization of class es; §2.3; [CG: C.2].
[6] Avoid ‘‘naked’’ union s; wrap them in a class together with a type field; §2.4; [CG: C.181].
[7] Use enumerations to represent sets of named constants; §2.5; [CG: Enum.2].
[8] Prefer class enum s over ‘‘plain’’ enum s to minimize surprises; §2.5; [CG: Enum.3].
[9] Define operations on enumerations for safe and simple use; §2.5; [CG: Enum.4].
Don’t interrupt me while I’m interrupting.
Exceptions; Invariants; Error-Handling Alternatives; Contracts; Static Assertions
• Function Arguments and Return Values
Argument Passing; Value Return; Structured Binding
Introduction
A C++ program is composed of various components, including functions, user-defined types, class hierarchies, and templates Effective management of these components relies on clearly defining their interactions, with a crucial distinction between an interface and its implementation In C++, interfaces are represented by declarations, which provide all necessary information to utilize a function or type For instance, the declaration of the square root function is represented as `double sqrt(double);`, indicating that it accepts a double and returns a double.
Vector(int s); double& operator[](int i); int size(); private: double ∗ elem; // elem points to an array of sz doubles int sz;
The main idea is that function definitions are located in a different context, referred to as "elsewhere." In this instance, we may also prefer the representation of Vector to be "elsewhere," which will be addressed later in the discussion of abstract types The definition of the square root function is expressed as follows: double sqrt(double d).
// algorithm as found in math textbook
For Vector , we need to define all three member functions:
Vector::Vector(int s) // definition of the constructor
:elem{new double[s]}, sz{s} // initialize members
} double& Vector::operator[](int i) // definition of subscripting
} int Vector::siz e() // definition of size()
To understand Vector's functions, it's essential to note that we won't define sqr t() as it is part of the standard library Nonetheless, this distinction is minor since a library consists of additional code written using the same language features we utilize.
There can be many declarations for an entity, such as a function, but only one definition.
Separate Compilation
C++ enables separate compilation, allowing user code to access only the declarations of types and functions, while their definitions reside in separate source files that are compiled independently This approach helps organize programs into semi-independent code fragments, reducing compilation times and enforcing clear separation between distinct program components, thereby minimizing errors Libraries often consist of these separately compiled code fragments, such as functions.
Typically, we place the declarations that specify the interface to a module in a file with a name indicating its intended use For example:
Vector(int s); double& operator[](int i); int size(); private: double ∗ elem; // elem points to an array of sz doubles int sz;
This declaration would be placed in a file Vector.h Users thenincludethat file, called aheader file, to access that interface For example:
#include "Vector.h" // get Vector’s interface
#include // get the standard-librar y math function interface including sqrt() double sqrt_sum(Vector& v)
{ double sum = 0; for (int i=0; i!=v.siz e(); ++i) sum+=std::sqr t(v[i]); // sum of square roots return sum;
To help the compiler ensure consistency, the cpp file providing the implementation of Vector will also include the h file providing its interface:
#include "Vector.h" // get Vector’s interface
:elem{new double[s]}, sz{s} // initialize members
The code in user.cpp and Vector.cpp utilizes the Vector interface defined in Vector.h; however, these two files are independent and can be compiled separately.
Vector.h : user.cpp : Vector.cpp :
Separate compilation is not inherently a language issue but rather concerns optimizing a specific language implementation It is crucial for effective program organization to view a program as a collection of modules with clear dependencies By logically representing this modularity through language features and physically organizing it into files, developers can maximize the benefits of separate compilation.
A cpp file that is compiled by itself (including the h files it #include s) is called atranslation unit A program can consist of many thousand translation units.
Modules (C++20)
The use of `#include` directives in programming is an outdated and error-prone method for assembling code from various components When a header file, such as `header.h`, is included in multiple translation units, it is processed by the compiler each time, leading to inefficiency Additionally, the order of including headers can cause conflicts, as the declarations and macros in one header can alter the behavior of another, resulting in potential bugs This issue has been a significant source of programming errors and costs since the introduction of this mechanism in C in 1972.
C++ is set to enhance the way physical modules are expressed with the introduction of modules, a feature that, while not yet part of ISO C++, is included in the ISO Technical Specification [ModulesTS] and will be incorporated in C++20 Although implementations are currently available, it's important to note that details may evolve, and widespread adoption in production code could take years Meanwhile, legacy code utilizing #include directives tends to persist due to the high costs and time requirements associated with updating it.
Consider how to express the Vector and sqr t_sum() example from §3.2 using module s:
// file Vector.cpp: module; // this compilation will define a module
// here we put stuff that Vector might need for its implementation
Section 3.3 Modules (C++20) 33 expor t module Vector; // defining the module called "Vector" expor t class Vector { public:
Vector(int s); double& operator[](int i); int size(); private: double ∗ elem; // elem points to an array of sz doubles int sz;
:elem{new double[s]}, sz{s} // initialize members
} expor t int size(const Vector& v) { return v.siz e(); }
This defines a module called Vector , which exports the class Vector , all its member functions, and the non-member function siz e()
The way we use this module is to impor t it where we need it For example:
// file user.cpp: impor t Vector; // get Vector’s interface
#include // get the standard-librar y math function interface including sqrt() double sqrt_sum(Vector& v)
{ double sum = 0; for (int i=0; i!=v.siz e(); ++i) sum+=std::sqr t(v[i]); // sum of square roots return sum;
I could have impor t ed the standard library mathematical functions also, but I used the old-fashioned
#include just to show that you can mix old and new Such mixing is essential for gradually upgrad- ing older code from using #include to impor t
The differences between headers and modules are not just syntactic.
• A module is compiled once only (rather than in each translation unit in which it is used).
• Two modules can be impor t ed in either order without changing their meaning.
• If you import something into a module, users of your module do not implicitly gain access to (and are not bothered by) what you imported: impor t is not transitive.
The effects on maintainability and compile-time performance can be spectacular.
Namespaces
C++ provides namespaces as a way to group related declarations and prevent naming conflicts, alongside features like functions, classes, and enumerations For instance, if I wish to create a custom complex number type, I can encapsulate it within a namespace, such as "My_code," to ensure its uniqueness and avoid clashes with other identifiers.
{ complex z {1,2}; auto z2 = sqrt(z); std::cout draw(); mouth−>draw();
Smiley maintains its eyes in a standard-library vector and removes them in its destructor, which overrides the virtual destructor of Shape A virtual destructor is crucial for abstract classes, as derived class objects are typically managed through their abstract base class interface and may be deleted via a base class pointer This mechanism guarantees that the appropriate destructor is invoked, which subsequently calls the destructors of its bases and members.
In this simplified example, it is the programmer’s task to place the eyes and mouth appropri- ately within the circle representing the face.
We can add data members, operations, or both as we define a new class by derivation This gives great flexibility with corresponding opportunities for confusion and poor design.
A class hierarchy offers two kinds of benefits:
Interface inheritance allows an object of a derived class to be utilized in place of an object from a base class, effectively making the base class serve as an interface for the derived class Examples of this concept can be seen in the Container and Shape classes, which are frequently categorized as abstract classes.
Implementation inheritance allows a base class to offer functions or data that facilitate the development of derived classes For instance, Smiley utilizes the Circle class's constructor and its draw() method, demonstrating how base classes typically include data members and constructors to streamline the implementation process for subclasses.
Concrete classes, particularly those with small representations, resemble built-in types as they can be defined as local variables and accessed by their names In contrast, classes within class hierarchies are typically allocated on the free store using the 'new' keyword and are accessed through pointers or references For instance, a function that reads shape data from an input stream can construct the corresponding Shape objects, utilizing an enumeration class to define different shapes such as circle, triangle, and smiley.
Shape ∗ read_shape(istream& is) // read shape descriptions from input stream is
// read shape header from is and find its Kind k switch (k) { case Kind::circle:
// read circle data {Point,int} into p and r return new Circle{p,r}; case Kind::triangle:
// read triangle data {Point,Point,Point} into p1, p2, and p3 return new Triangle{p1,p2,p3}; case Kind::smiley:
// read smiley data {Point,int,Shape,Shape,Shape} into p, r, e1, e2, and m
Smiley ∗ ps = new Smiley{p,r}; ps−>add_eye(e1); ps−>add_eye(e2); ps−>set_mouth(m); return ps;
A program may use that shape reader like this:
Section 4.5.1 Benefits from Hierarchies 61 void user()
In this code snippet, a vector of Shape pointers is created to store shapes read from standard input The program continuously reads shapes until input ends, subsequently drawing all shapes and rotating them by 45 degrees Finally, it ensures memory management by deleting each shape pointer in the vector to prevent memory leaks.
The user() function operates independently of the specific shapes it manipulates, allowing for future Shape additions without requiring recompilation It is responsible for deallocating shapes using the delete operator, which is essential for proper memory management This process relies on the virtual destructor of the Shape class, ensuring that the destructor of the most derived class is called, enabling the release of resources acquired by derived classes, such as file handles and locks For instance, a Smiley object will delete its eye and mouth components before invoking the destructor of the Circle class In this context, objects are constructed in a "bottom-up" manner, with base classes initialized first, while destructors operate in a "top-down" fashion, destroying derived classes first.
The read_shape() function returns a pointer to Shape, allowing us to handle all Shapes uniformly To access a member function exclusive to a specific derived class, like Smiley's wink(), we can utilize the dynamic_cast operator to check if the Shape is indeed a Smiley.
Shape ∗ ps {read_shape(cin)}; if (Smiley ∗ p = dynamic_cast(ps)) { // does ps point to a Smiley?
// not a Smiley, try something else
During runtime, if the object referenced by the dynamic_cast argument (in this case, ps) is not of the anticipated type (Smiley) or a derived class of that type, dynamic_cast will return nullptr.
Dynamic_cast is utilized for converting a pointer to a different derived class, allowing for safe type checking in C++ To determine if the cast was successful, it's essential to check if the result is nullptr This check can conveniently be integrated into the variable initialization within a conditional statement.
When a different type is unacceptable, we can simply dynamic_cast to a reference type If the object is not of the expected type, dynamic_cast throws a bad_cast exception:
Shape ∗ ps {read_shape(cin)};
Smiley& r {dynamic_cast( ∗ ps)}; // somewhere, catch std::bad_cast
Using dynamic_cast judiciously leads to cleaner code, as it allows for simpler and more efficient programming by minimizing reliance on type information However, there are instances where type information is lost, particularly when an object is passed to a system that operates on a base class interface In such cases, recovering the original type becomes necessary when the object is returned Operations akin to dynamic_cast include "is kind of" and "is instance of" checks.
Experienced programmers will have noticed that I left open three opportunities for mistakes:
• The implementer of Smiley may fail to delete the pointer to mouth
• A user of read_shape() might fail to delete the pointer returned.
• The owner of a container of Shape pointers might fail to delete the objects pointed to.
In that sense, pointers to objects allocated on the free store is dangerous: a ‘‘plain old pointer’’ should not be used to represent ownership For example: void user(int x)
// if (x> and = , and make sure that the usual equivalences hold:
To ensure consistent treatment of both operands in a binary operator like ==, it is advisable to define it as a free-standing function within the class's namespace For instance, consider the following implementation within the NX namespace: class X.
When designing containers, it is advisable to follow the style of standard-library containers unless there are compelling reasons not to It is crucial to ensure that the container is resource safe by implementing it as a handle that includes the necessary essential operations.
Standard library containers maintain a count of their elements, which can be accessed using the size() function For instance, you can iterate through the container with a loop, utilizing size_t as the type returned by size(), to initialize each element to zero: for (size_t i = 0; i < c.size(); ++i) c[i] = 0;
However, rather than traversing containers using indices from 0 to siz e() , the standard algorithms (Chapter 12) rely on the notion ofsequences delimited by pairs ofiterators: for (auto p = c.begin(); p!=c.end(); ++p)
In C++, iterators are crucial for navigating through containers, with c.begin() pointing to the first element and c.end() indicating the position just beyond the last element Similar to pointers, iterators allow for incrementing with ++ to access the next element and dereferencing with * to retrieve the value This iterator model enhances both generality and efficiency, enabling the seamless passing of sequences to standard-library algorithms, such as sort(v.begin(), v.end()).
For details and more container operations, see Chapter 11 and Chapter 12.
Another way of using the number of elements implicitly is a range- for loop: for (auto& x : c) x = 0;
This uses c.begin() and c.end() implicitly and is roughly equivalent to the more explicit loop.
In the context of integer pairs, the symbols > represent left-shift and right-shift operations, respectively In contrast, when dealing with iostreams, these symbols function as output and input operators For further information and additional I/O operations, please refer to Chapter 10.
Classes allow programmers to create and implement custom types that closely resemble built-in types Constructors offer a level of initialization that matches or surpasses the flexibility and efficiency of built-in type initialization, which is typically achieved through literals.
• 0xFF00u is an unsigned int
It can be useful to provide such literals for a user-defined type also This is done by defining the meaning of a suitable suffix to a literal, so we can get
• 12.7i is imaginar y so that 12.7i+47 is a complex number (i.e., {47,12.7} ).
In particular, we can get these examples from the standard library by using suitable headers and namespaces:
Standard-Library Suffixes for Literals
std::literals::chrono_literals h , min , s , ms , us , ns
std::literals::string_literals s
std::literals::string_literals sv
std::literals::complex_literals i , il , if
User -defined literals (UDLs) are literals that feature custom suffixes and are created using literal operators These operators convert a literal of a specified argument type, followed by a subscript, into a defined return type For instance, an imaginary suffix 'i' can be implemented as follows: constexpr complex operator""i(long double arg), which represents an imaginary literal.
• The operator"" indicates that we are defining a literal operator.
• The i after the ‘‘literal indicator’’ "" is the suffix to which the operator gives a meaning.
• The argument type, long double , indicates that the suffix ( i ) is being defined for a floating- point literal.
• The return type, complex , specifies the type of the resulting literal.
Given that, we can write complex z = 2.7182818+6.283185i;
Many algorithms, particularly sort(), rely on a swap() function to exchange the values of two objects, typically assuming that this operation is fast and does not throw exceptions The standard library offers a std::swap(a, b) function, which is implemented using three move operations: (tmp = a, a = b, b = tmp) If you create a type that is costly to copy and could be swapped, such as for sorting, it is advisable to implement move operations, a swap() function, or both Additionally, standard library containers and strings are designed with efficient move operations.
The standard-library unordered_map functions as a hash table, where K represents the key type and V denotes the value type To utilize a specific type X as a key, it is necessary to define hash, although the standard library automatically provides this for common types like std::string.
Advice
[1] Control construction, copy, move, and destruction of objects; §5.1.1; [CG: R.1].
[2] Design constructors, assignments, and the destructor as a matched set of operations; §5.1.1; [CG: C.22].
[3] Define all essential operations or none; §5.1.1; [CG: C.21].
[4] If a default constructor, assignment, or destructor is appropriate, let the compiler generate it (don’t rewrite it yourself); §5.1.1; [CG: C.20].
[5] If a class has a pointer member, consider if it needs a user-defined or deleted destructor, copy and move; §5.1.1; [CG: C.32] [CG: C.33].
[6] If a class has a user-defined destructor, it probably needs user-defined or deleted copy and move; §5.2.1.
[7] By default, declare single-argument constructors explicit ; §5.1.1; [CG: C.46].
[8] If a class member has a reasonable default value, provide it as a data member initializer; §5.1.3; [CG: C.48].
[9] Redefine or prohibit copying if the default is not appropriate for a type; §5.2.1, §4.6.5; [CG: C.61].
[10] Return containers by value (relying on move for efficiency); §5.2.2; [CG: F.20].
[11] For large operands, use const reference argument types; §5.2.2; [CG: F.16].
[12] Provide strong resource safety; that is, never leak anything that you think of as a resource; §5.3; [CG: R.1].
[13] If a class is a resource handle, it needs a user-defined constructor, a destructor, and non- default copy operations; §5.3; [CG: R.1].
[14] Overload operations to mimic conventional usage; §5.4; [CG: C.160].
[15] Follow the standard-library container design; §5.4.2; [CG: C.100].
Constrained Template Arguments; Value Template Arguments; Template Argument Deduction
Function Templates; Function Objects; Lambda Expressions
Variable Templates; Aliases; Compile-Time if
Introduction
A vector is a versatile concept that is not limited to a specific element type, such as double Instead, it should be defined independently of floating-point numbers Templates play a crucial role in this context, allowing us to create classes or functions that can be parameterized with various types or values By using templates, we can effectively represent general ideas and generate specific types and functions by specifying the desired element type, such as double for a vector.
Parameterized Types
We can enhance our vector-of-doubles type into a versatile vector-of-anything type by implementing a template and substituting the specific type double with a type parameter For instance, we can define it as: template class Vector { private:
The `Vector` class is designed to manage an array of elements of type T, with a specified size `sz` It includes a constructor that initializes the array and acquires necessary resources, while the destructor ensures proper resource release by deleting the allocated array.
T& operator[](int i); // for non-const Vectors const T& operator[](int i) const; // for const Vectors (§4.2.1) int size() const { return sz; }
In C++, the template prefix designates T as a type parameter, analogous to the mathematical expression "for all T" or "for all types T." To express "for all T, such that P(T)," one must utilize concepts Additionally, using class to introduce a type parameter is functionally equivalent to using typename, which is why older code frequently employs template as the prefix.
The member functions might be defined similarly: template
{ if (s= with the appropriate semantics.
A concept goes beyond mere syntax; it is fundamentally rooted in semantics For instance, defining "add" as "to divide" fails to meet the criteria for any sensible number Currently, we lack language support for expressing semantics, which necessitates relying on expert knowledge and common sense to create meaningful concepts Avoid defining semantically empty terms like "Addable" and "Subtractable." Instead, utilize domain knowledge to establish concepts that align with the core principles of a specific application domain.
Good abstractions are carefully grown from concrete examples It is not a good idea to try to
To avoid code bloat and inel egance, focus on concrete examples rather than preparing for every possible need and technique Begin with a specific function, such as double sum(const vector& v), and work to eliminate unnecessary details for clarity and efficiency.
{ double res = 0; for (auto x : v) res += x; return res;
This is obviously one of many ways to compute the sum of a sequence of numbers.
Consider what makes this code less general than it needs to be:
Answering the first four questions by making the concrete types into template arguments, we get the simplest form of the standard-library accumulate algorithm: template
Val accumulate(Iter first, Iter last, Val res)
{ for (auto p = first; p!=last; ++p) res += ∗ p; return res;
• The data structure to be traversed has been abstracted into a pair of iterators representing a sequence (§12.1).
• The type of the accumulator has been made into a parameter.
• The initial value is now an input; the type of the accumulator is the type of this initial value.
A brief analysis or measurement reveals that the code produced for function calls using different data structures matches the output of the manually coded original example For instance, the function signature void use(const vector& vec, const list& lst) demonstrates this consistency.
{ auto sum = accumulate(begin(vec),end(vec),0.0); // accumulate in a double auto sum2 = accumulate(begin(lst),end(lst),sum);
The process of generalizing from a concrete piece of code (and preferably from several) while pre- serving performance is calledlifting Conversely, the best way to develop a template is often to
• then, debug, test, and measure it
• finally, replace the concrete types with template arguments.
Naturally, the repetition of begin() and end() is tedious, so we can simplify the user interface a bit: template // a Range is something with begin() and end()
Val accumulate(const R& r, Val res = 0)
{ for (auto p = begin(r); p!=end(r); ++p) res += ∗ p; return res;
For full generality, we can abstract the += operation also; see §14.3.
Variadic Templates
A variadic template allows for the acceptance of an arbitrary number of arguments of various types This flexibility enables the creation of functions that can output values of any type that supports the That would be cor- rect behavior, but unlikely what the programmer wanted.
For a more exhaustive presentation of regular expressions, see [Friedl,1997].
A regex_iterator is used to iterate through a sequence of characters to find matches for a specific pattern For instance, utilizing a regex_iterator allows us to extract and output all whitespace-separated words from a given string.
{ string input = "aa as; asd ++eˆasdf asdfg"; reg ex pat {R"(\s+(\w+))"}; for (sreg ex_iterator p(input.begin(),input.end(),pat); p!=sregex_iterator{}; ++p) cout f() A lambda function effectively bridges the gap between these two different notations.
Given a member function, the function adaptor mem_fn(mf) produces a function object that can be called as a nonmember function For example: void draw_all(vector& v)
{ for_each(v.begin(),v.end(),mem_fn(&Shape::draw));
Before the introduction of lambdas in C++11, mem_fn() and equivalents were the main way to map from the object-oriented calling style to the functional one.
The standard-library function type can encapsulate any object that can be invoked using the call operator () Specifically, an object of type function represents a function object For instance, the function int f1(double) can be initialized as function fct1 {f1}, while int f2(string) corresponds to function fct2 Additionally, a lambda expression like function fct3 = [](Shape * p) { p->draw(); } defines fct3 with the type function.
For fct2 , I let the type of the function be deduced from the initializer: int(string)
Functions are valuable for callbacks, passing operations as arguments, and utilizing function objects However, they can incur some runtime overhead compared to direct calls, and since functions are treated as objects, they do not support overloading For those needing to overload function objects, including lambdas, refer to the section on overloading.
Type Functions
A type function is a compile-time function that takes a type as an argument or returns a type The standard library offers various type functions to assist library implementers and programmers in leveraging the features of the language and standard library effectively.
For numerical types, numeric_limits from presents a variety of useful information (§14.7) For example: constexpr float min = numeric_limits::min(); // smallest positive float
Similarly, object sizes can be found by the built-in siz eof operator (§1.4) For example: constexpr int szi = sizeof(int); // the number of bytes in an int
C++ includes mechanisms for compile-time computation that enhance type checking and improve performance, commonly referred to as metaprogramming or template metaprogramming when templates are utilized This article focuses on two features from the standard library, specifically iterator_traits.
Type predicates and concepts play a crucial role in simplifying various techniques, although concepts are not yet universally adopted As a result, the techniques discussed remain widely utilized in programming practices.
The standard library's sort() function requires a pair of random-access iterators to define a sequence However, certain containers, like forward_list, do not provide this capability due to their singly-linked list structure, which makes subscripting inefficient and prevents easy access to previous elements Despite this limitation, forward_list does offer forward iterators that allow traversal of the sequence using algorithms and for-statements.
The standard library features a mechanism called iterator_traits, enabling us to determine the type of iterator in use This capability allows us to enhance the range sort() function from §12.8, enabling it to accept both vectors and forward_lists For instance, we can implement a function like void test(vector& v, forward_list& lst) to demonstrate this versatility.
{ sor t(v); // sor t the vector sor t(lst); // sor t the singly-linked list
The techniques needed to make that work are generally useful.
I create two helper functions that include an additional parameter to specify their use with either random-access iterators or forward iterators The implementation for the function designed for random-access iterators is straightforward, allowing subscripting within the specified range.
{ sor t(beg,end); // just sort it
The forward iterator version of the sort_helper function efficiently copies a list into a vector, sorts the vector, and then copies the sorted elements back into the original list This process utilizes the forward_iterator_tag to enable traversal of the specified range from beg to end.
{ vector v {beg,end}; // initialize a vector from [beg:end) sor t(v.begin(),v.end()); // use the random access sort copy(v.begin(),v.end(),beg); // copy the elements back
The value type of a For iterator's elements is referred to as Value_type Each standard-library iterator includes a member called value_type This notation can be defined using a type alias, as shown in the following template: `template using Value_type = typename C::value_type;`, which represents the value type of the container C.
Thus, for a vector , Value_type is X
The real ‘‘type magic’’ is in the selection of helper functions: template void sort(C& c)
{ using Iter = Iterator_type; sor t_helper(c.begin(),c.end(),Iterator_category{});
Here, I use two type functions: Iterator_type returns the iterator type of C (that is, C::iterator ) and then Iterator_categor y{} constructs a ‘‘tag’’ value indicating the kind of iterator provided:
• std::random_access_iterator_tag if C ’s iterator supports random access
• std::forward_iterator_tag if C ’s iterator supports forward iteration
We can choose between two sorting algorithms at compile time using a technique known as tag dispatch This method is one of several employed in the standard library and other applications to enhance flexibility and performance.
We could define Iterator_type like this: template using Iterator_type = typename C::iterator; // C’s iterator type
To apply tag dispatch to types lacking member types, such as pointers, the standard library offers the class template iterator_traits from The specialization for pointers is defined as follows:
Section 13.9.1 iterator_traits 183 template struct iterator_traits { using difference_type = ptrdiff_t; using value_type = T; using pointer = T ∗ ; using reference = T&; using iterator_category = random_access_iterator_tag;
We can now write: template using Iterator_category = typename std::iterator_traits::iterator_category; // Iter’s categor y
Now an int ∗can be used as a random-access iterator despite not having a member type; Iterator_cat- egor y is random_access_iterator_tag
Many traits and traits-based techniques may become obsolete due to emerging concepts in C++ For instance, the concepts version of the sort() function illustrates this shift: `template void sort(Iter p, Iter q);` is applicable for `std::vector` and other random access types, while `template void sort(Iter p, Iter q)` caters to forward iterators.
// use for std::list and other types supporting just forward traversal
{ vector v {p,q}; sor t(v); // use the random-access sort copy(v.begin(),v.end(),p);
{ sor t(r.begin(),r.end()); // use the appropriate sort
The header in the standard library provides essential type predicates that determine fundamental properties of types For instance, using `std::is_arithmetic()` returns true, confirming that `int` is an arithmetic type, while `std::is_arithmetic()` returns false, indicating that `std::string` is not an arithmetic type.
In template programming, several type traits such as is_class, is_pod, is_literal_type, has_virtual_destructor, and is_base_of play a crucial role These traits enhance the functionality and flexibility of templates, allowing developers to create more robust and type-safe code For instance, consider the template definition: `template class complex {`.
Scalar re, im; public: static_asser t(is_arithmetic(), "Sorr y, I only suppor t complex of arithmetic types"); //
To improve readability, the standard library defines template aliases For example: template constexpr bool is_arithmetic_v = std::is_arithmetic();
While I'm not particularly fond of the _v suffix notation, the method for creating aliases is highly beneficial For instance, the standard library describes the Regular concept as follows: template concept Regular = Semiregular && EqualityComparable;
Type predicates can be effectively utilized in static asserts, compile-time if statements, and enable_if constructs The standard-library enable_if serves as a popular method for conditionally defining templates For instance, when defining a "smart pointer," one might implement it as follows: template class Smart_pointer {.
T& operator−>(); // -> should wor k if and only if T is a class
The −> should be defined if and only if T is a class type For example, Smar t_pointer should have −> , but Smar t_pointer should not.
We cannot use a compile-time if because we are not inside a function Instead, we write template class Smart_pointer {
T& operator ∗ (); std::enable_if operator−>(); // -> is defined if and only if T is a class
If is_class() is true , the return type of operator−>() is T& ; otherwise, the definition of operator−>() is ignored.
Advice
[1] A library doesn’t hav e to be large or complicated to be useful; §13.1.
[2] A resource is anything that has to be acquired and (explicitly or implicitly) released; §13.2.
[3] Use resource handles to manage resources (RAII); §13.2; [CG: R.1].
[4] Use unique_ptr to refer to objects of polymorphic type; §13.2.1; [CG: R.20].
[5] Use shared_ptr to refer to shared objects (only); §13.2.1; [CG: R.20].
[6] Prefer resource handles with specific semantics to smart pointers; §13.2.1.
[7] Prefer unique_ptr to shared_ptr ; §5.3, §13.2.1.
[8] Use make_unique() to construct unique_ptr s; §13.2.1; [CG: R.22].
[9] Use make_shared() to construct shared_ptr s; §13.2.1; [CG: R.23].
[10] Prefer smart pointers to garbage collection; §5.3, §13.2.1.
[11] Don’t use std::move(); §13.2.2; [CG: ES.56].
[12] Use std::forward() exclusively for forwarding; §13.2.2.
[13] Never read from an object after std::move() ing or std::forward() ing it; §13.2.2.
[14] Prefer span s to pointer-plus-count interfaces; §13.3; [CG: F.24].
[15] Use array where you need a sequence with a constexpr size; §13.4.1.
[16] Prefer array over built-in arrays; §13.4.1; [CG: SL.con.2].
[17] Use bitset if you need N bits and N is not necessarily the number of bits in a built-in integer type; §13.4.2.
[18] Don’t overuse pair and tuple ; named struct s often lead to more readable code; §13.4.3.
[19] When using pair , use template argument deduction or make_pair() to avoid redundant type specification; §13.4.3.
[20] When using tuple , use template argument deduction and make_tuple() to avoid redundant type specification; §13.4.3; [CG: T.44].
[21] Prefer variant to explicit use of union s; §13.5.1; [CG: C.181].
[22] Use allocators to prevent memory fragmentation; §13.6.
[23] Time your programs before making claims about efficiency; §13.7.
[24] Use duration_cast to report time measurements with proper units; §13.7.
[25] When specifying a duration , use proper units; §13.7.
[26] Use mem_fn() or a lambda to create function objects that can invoke a member function when called using the traditional function call notation; §13.8.2.
[27] Use function when you need to store something that can be called; §13.8.3.
[28] You can write code to explicitly depend on properties of types; §13.9.
[29] Prefer concepts over traits and enable_if whenever you can; §13.9.
[30] Use aliases and type predicates to simplify notation; §13.9.1, §13.9.2.
The purpose of computing is insight, not numbers.
but for the student, numbers are often the best road to insight.
Introduction
C++ was not originally designed for numeric computation, but its versatility makes it suitable for various applications, including database access, networking, instrument control, graphics, simulation, and financial analysis As numeric methods have evolved beyond simple loops over vectors, C++'s strengths in handling complex data structures become increasingly relevant As a result, C++ is extensively utilized in scientific, engineering, and financial computations that require advanced numeric techniques This chapter outlines the components of the standard library that facilitate numeric computations.
Mathematical Functions
In , we find the standard mathematical functions, such as sqr t() , log() , and sin() for argu- ments of type float , double , and long double :
Standard mathematical functions include abs(x) for absolute value, ceil(x) which gives the smallest integer greater than or equal to x, and floor(x) that returns the largest integer less than or equal to x The sqr t(x) function calculates the square root of x, provided x is non-negative Trigonometric functions such as cos(x), sin(x), and tan(x) compute the cosine, sine, and tangent of x, respectively Inverse trigonometric functions include acos(x) for arccosine, returning a non-negative result, asin(x) for arcsine, which gives the result closest to 0, and atan(x) for arctangent Hyperbolic functions are represented by sinh(x) for hyperbolic sine, cosh(x) for hyperbolic cosine, and tanh(x) for hyperbolic tangent The exp(x) function calculates the base e exponential, while log(x) computes the natural logarithm with base e, applicable only for positive x Additionally, log10(x) provides the base 10 logarithm.
The versions for complex (§14.4) are found in For each function, the return type is the same as the argument type.
Errors are reported by setting errno from to EDOM for a domain error and to ERANGE for a range error For example: void f()
When working with mathematical functions in programming, it's essential to manage error states effectively For instance, before calculating the square root of a negative number, it's crucial to clear the old error state, as indicated by setting `errno = 0` If the square root function encounters a negative argument, it will not be defined, prompting an error message Similarly, when using the power function to compute a value that exceeds the maximum limit for doubles, clearing the error state again is necessary If the result is too large to represent, the function will set `errno` to `ERANGE`, leading to an appropriate error notification.
A few more mathematical functions are found in and the so-calledspecial mathematical functions, such as beta() , rieman_z eta() , and sph_bessel() , are also in
Numerical Algorithms
In , we find a small set of generalized numerical algorithms, such as accumulate()
Numerical algorithms offer various functions for mathematical operations on sequences and arrays The function `xcumulate(b, e, i)` computes the sum of the elements from index `b` to `e` along with an initial value `i` For customized accumulation, `xcumulate(b, e, i, f)` allows the use of a different operation `f` instead of addition The `inner_product(b, e, b2, i)` function calculates the inner product of two ranges, combining elements from the first range `[b:e)` and a corresponding second range `[b2:b2+(e−b)]`, starting with an initial value `i` This can be extended with `inner_product(b, e, b2, i, f, f2)` to apply different operations for addition and multiplication The `partial_sum(b, e, out)` function generates a cumulative sum, storing results in the output array `out`, while `partial_sum(b, e, out, f)` allows for a custom operation The `adjacent_difference(b, e, out)` function computes the difference between consecutive elements, with `adjacent_difference(b, e, out, f)` enabling a custom operation for differences The `iota(b, e, v)` function assigns sequential values starting from `v`, resulting in a series like `v+1, v+2, ` Additionally, the `gcd(n, m)` function determines the greatest common divisor, and `lcm(n, m)` finds the least common multiple of two integers.
Algorithms can generalize common operations like summation, allowing them to be applied to various sequences They also enable the operation performed on sequence elements to be specified as a parameter Each algorithm typically includes a general version along with a specific version that uses the most common operator For instance, using the `accumulate` function on a list of doubles, such as `list lst {1, 2, 3, 4, 5, 9999.99999};`, allows for the calculation of the sum, resulting in a total of 10014.9999.
These algorithms work for every standard-library sequence and can have operations supplied as arguments (§14.3).
In , the numerical algorithms have parallel versions (§12.9) that are slightly different:
Parallel numerical algorithms utilize functions such as `reduce` and `exclusive_scan` to perform operations on data The `reduce` function can be executed with various parameters, including specifying a value type and an execution policy The `exclusive_scan` function computes the partial sum of elements, excluding the current element from the total, while `inclusive_scan` includes the current element in the calculation Both operations can be customized using an execution policy to optimize performance.
Parallel Numerical Algorithms involve several key functions for data transformation and reduction The `transform_reduce` function applies a specified operation `f(x)` to each element `x` in the range `[b : e)`, followed by a reduction process Similarly, `transform_exclusive_scan` processes the same range with `f(x)` and performs an exclusive scan, while `transform_inclusive_scan` applies `f(x)` and executes an inclusive scan Each of these functions enhances performance in parallel computing by efficiently managing data transformations and reductions.
In this article, I focus on the core versions of algorithms that utilize basic operations like addition and equality, intentionally excluding those that incorporate functor arguments Additionally, I omit the variations that apply default policies, such as sequential execution, and default values, with the exception of the reduce() function.
To implement parallel algorithms in C++, an execution policy can be specified For example, using a vector of doubles, we can calculate the sum of its elements with the `reduce` function: `vector v {1, 2, 3, 4, 5, 9999.99999}; auto s = reduce(v.begin(), v.end());` This method utilizes a double as the accumulator for the summation Additionally, a vector named `large` can be declared for further operations.
// fill large with lots of values auto s2 = reduce(par_unseq,large begin(),large end()); // calculate the sum using available parallelism
The parallel algorithms (e.g., reduce() ) differ from the sequentional ones (e.g., accumulate() ) by allowing operations on elements in unspecified order.
Complex Numbers
The standard library includes a variety of complex number types similar to the complex class outlined in §4.2.1 To accommodate complex numbers with different scalar types, such as single-precision (floats) and double-precision (doubles) floating-point numbers, the standard library utilizes a template structure: template class complex { public: complex(const Scalar& re ={}, const Scalar& im ={}); // default function arguments; see §3.6.1.
The usual arithmetic operations and the most common mathematical functions are supported for complex numbers For example: void f(complex fl, complex db)
{ complex ld {fl+sqrt(db)}; db += fl ∗ 3; fl = pow(1/fl,2);
The sqr t() and pow() (exponentiation) functions are among the usual mathematical functions defined in (§14.2).
Random Numbers
Random numbers play a crucial role in various fields, including testing, gaming, simulation, and security The extensive range of applications is demonstrated by the diverse array of random number generators available in the standard library of A random number generator is comprised of two main components.
[1] Anenginethat produces a sequence of random or pseudo-random values
[2] Adistributionthat maps those values into a mathematical distribution in a range
In random number generation, various distributions are utilized, such as uniform_int_distribution, which ensures all integers are equally likely; normal_distribution, known as "the bell curve"; and exponential_distribution, which models exponential growth within a specified range For instance, by defining a default_random_engine and a uniform_int_distribution that maps integers from 1 to 6, one can create a generator function to simulate rolling a die, where the outcome is a random integer between 1 and 6.
The standard-library random number component is praised for its exceptional generality and performance, earning it the reputation of being the ideal model for random number libraries However, it may not be considered user-friendly for beginners The inclusion of using statements and lambda functions helps clarify its functionality.
For beginners from any background, the comprehensive interface of the random number library can be quite challenging Often, a basic uniform random number generator is all that's needed to begin.
Rand_int rnd {1,10}; // make a random number generator for [1:10] int x = rnd(); // x is a number in [1:10]
So, how could we get that? We hav e to get something that, like die() , combines an engine with a distribution inside a class Rand_int : class Rand_int { public:
The `Rand_int` class generates random integers within a specified range, defined by the parameters `low` and `high` It utilizes a uniform integer distribution for generating random values and allows users to draw an integer by calling the `operator()` Additionally, the class provides a method to set a new seed for the random engine, ensuring varied random number generation The implementation includes a default random engine and a uniform distribution to maintain randomness and consistency in generated values.
That definition is still ‘‘expert level,’’ but theuseof Rand_int() is manageable in the first week of aC++ course for novices For example: int main()
Rand_int rnd {0,max}; // make a unifor m random number generator vector histogram(max+1); // make a vector of appropriate size for (int i=0; i! 0; ++i)
++histogram[rnd()]; // fill histogram with the frequencies of numbers [0:max] for (int i = 0; i!=histogram.size(); ++i) { // wr ite out a bar graph cout > (no space between the > s)
[17] Alignment controls: alignas and alignof
[18] The ability to use the type of an expression as a type in a declaration: decltype
[20] Generalized POD (‘‘Plain Old Data’’)
[22] Local classes as template arguments
[24] A syntax for attributes and two standard attributes: [[carries_dependency]] and [[noreturn]]
[25] Preventing exception propagation: the noexcept specifier (§3.5.1)
[26] Testing for the possibility of a throw in an expression: the noexcept operator.
[27] C99 features: extended integral types (i.e., rules for optional longer integer types); con- catenation of narrow/wide strings; STDC_HOSTED ; _Pragma(X) ; vararg macros and empty macro arguments
[28] func as the name of a string holding the name of the current function
[32] Control of defaults: default and delete (§5.1.1)
[35] More explicit control of template instantiation: extern template s
[36] Default template arguments for function templates
[38] Override controls: override and final (§4.5.1)
[39] A simpler and more general SFINAE (Substitution Failure Is Not An Error) rule
[41] Thread-local storage: thread_local
For a more complete description of the changes to C++98 in C++11, see [Stroustrup,2013].
[2] Improved constexpr functions, e.g., for -loops allowed (§1.6)
[2] Dynamic allocation of over-aligned types
[7] Generic value template arguments ( auto template parameters)
[8] Class template argument type deduction (§6.2.3)
[14] New standard attributes: [[fallthrough]] , [[nodiscard]] , and [[maybe_unused]]
[16] Initialization of an enum by a value of its underlying type (§2.5)
C++11 enhances the standard library by introducing new components like the regular expression matching library and improving existing C++98 features, including the addition of move constructors for containers.
[1] initializ er_list constructors for containers (§4.2.3)
[3] A singly-linked list: forward_list (§11.6)
[4] Hash containers: unordered_map , unordered_multimap , unordered_set , and unordered_mul- tiset (§11.6, §11.5)
[5] Resource management pointers: unique_ptr , shared_ptr , and weak_ptr (§13.2.1)
[6] Concurrency support: thread (§15.2), mutexes (§15.5), locks (§15.5), and condition vari- ables (§15.6)
[7] Higher-level concurrency support: packaged_thread , future , promise , and async() (§15.7)
[10] Random numbers: distributions and engines (§14.5)
[11] Integer type names, such as int16_t , uint32_t , and int_fast64_t
[12] A fixed-sized contiguous sequence container: array (§13.4.1)
[14] Error reporting using error codes: system_error
[16] Wide use of constexpr functions
[17] Systematic use of noexcept functions
[18] Improved function adaptors: function and bind() (§13.8)
[19] string to numeric value conversions
[21] Type traits, such as is_integral and is_base_of (§13.9.2)
[22] Time utilities: duration and time_point (§13.7)
[23] Compile-time rational arithmetic: ratio
[25] More algorithms, such as move() , copy_if() , and is_sor ted() (Chapter 12)
[27] Low-level concurrency support: atomic s
[9] Elementary string conversions: to_chars and from_chars
The vast number of existing C++ lines of code makes it challenging to identify which features are essential for critical applications As a result, the ISO committee is cautious about removing older features, doing so only after extensive warnings and deliberation Nevertheless, certain problematic features may still be eliminated from the language.
• C++17 finally removed exceptions specifications: void f() throw(X,Y); // C++98; now an error
The support facilities for exception specifications, unexcepted_handler , set_unexpected() , get_unexpected() , and unexpected() , are similarly removed Instead, use noexcept
• Trigraphs are no longer supported.
• The auto_ptr is deprecated Instead, use unique_ptr (§13.2.1).
• The use of the storage specifier register is removed.
• The use of ++ on a bool is removed.
• The C++98 expor t feature was removed because it was complex and not shipped by the major vendors Instead, expor t is used as a keyword for modules (§3.3).
• Generation of copy operations is deprecated for a class with a destructor (§5.1.1).
• Assignment of a string literal to a char ∗is removed Instead use const char ∗or auto
• Some C++ standard-library function objects and associated functions are deprecated Most relate to argument binding Instead use lambdas and function (§13.8).
Deprecating a feature indicates the standards committee's intention for it to be phased out, although immediate removal is not mandated, even for features that may be redundant or risky This serves as a warning to developers to avoid using the feature, as it may be removed in the future Compilers often issue warnings when deprecated features are utilized, yet these features remain part of the standard and, historically, tend to be supported indefinitely for compatibility reasons.
C/C++ Compatibility
C++ is primarily a superset of C, specifically C11, with most differences arising from C++’s stronger focus on type checking Programs written in C often align closely with C++ standards, and compilers are equipped to identify all distinctions between the two languages For details on the incompatibilities between C99 and C++11, refer to Appendix C of the standard.
Classic C has two primary descendants: ISO C and ISO C++ These languages have evolved at varying rates and in distinct directions, leading to differences in their support for traditional C-style programming Consequently, these incompatibilities can create challenges for developers who work with both C and C++, especially when using libraries from one language in the other, as well as for those creating libraries and tools for both languages.
How can I call C and C++ siblings? Look at a simplified family tree:
A solid line indicates a strong inheritance of features, while a dashed line signifies a borrowing of major features, and a dotted line denotes a borrowing of minor features ISO C and ISO C++ are the two primary descendants of K&R C, sharing key elements of Classic C but lacking full compatibility The term "Classic C" originates from a sticker once placed on Dennis Ritchie's terminal, representing K&R C with added enumerations and struct assignment Additionally, BCPL is defined by Richards (1980), and C89 is outlined in C1990.
The differences between C and C++ are not solely due to modifications in C made by C++ Many incompatibilities stem from features that were integrated into C long after they were already established in C++, such as the assignment of a T* to a void* and the linkage of global constants Additionally, some features were adopted into C in an incompatible manner even after they were included in the ISO C++ standard, exemplified by the nuances of the inline keyword.
C and C++ have several minor incompatibilities that can pose challenges for programmers; however, these issues can be effectively managed within the C++ context Additionally, C code fragments can be compiled separately and linked using the extern "C" mechanism, facilitating interoperability between the two languages.
The major problems for converting a C program to C++ are likely to be:
• Suboptimal design and programming style.
• A void ∗implicitly converted to a T ∗(that is, converted without a cast).
• C++ keywords, such as class and private , used as identifiers in C code.
• Incompatible linkage of code fragments compiled as C and fragments compiled as C++.
A C program typically follows the K&R style, characterized by extensive use of pointers, arrays, and macros, which can complicate reliability in larger applications Resource management and error handling often lack robust documentation and support, leading to incomplete adherence Converting a C program to C++ may improve error checking, but it often reveals existing bugs without altering the core structure or addressing fundamental issues like incomplete error handling, resource leaks, or buffer overflows To achieve significant improvements, it is essential to restructure the code fundamentally.
C++ should not be viewed merely as an enhanced version of C; while it can be utilized in that manner, doing so limits its potential To fully leverage the significant advantages that C++ offers over C, it's essential to adopt distinct design and implementation approaches.
Leverage the C++ standard library to learn new programming techniques and styles, distinguishing it from the C standard library by utilizing modern features such as the assignment operator (=) instead of strcpy() for copying strings, and the equality operator (==) instead of strcmp() for comparisons.
In C++, macro substitution is rarely needed; instead, utilize `const`, `constexpr`, `enum`, or `enum class` to define manifest constants To eliminate function-calling overhead, use `inline` functions For specifying families of functions and types, implement templates, and to prevent name clashes, leverage namespaces.
[4] Don’t declare a variable before you need it and initialize it immediately A declaration can occur anywhere a statement can (§1.8), in for -statement initializers (§1.7), and in con- ditions (§4.5.2).
[5] Don’t use malloc() The new operator (§4.2.2) does the same job better, and instead of realloc() , try a vector (§4.2.3, §12.1) Don’t just replace malloc() and free() with ‘‘naked’’ new and delete (§4.2.2).
To optimize code quality and performance, it is advisable to avoid using void pointers, unions, and casts, reserving them only for deep implementation details within specific functions or classes These practices can restrict the benefits of the type system and may indicate underlying design flaws.
[7] If you must use an explicit type conversion, use an appropriate named cast (e.g., static_cast ; §16.2.7) for a more precise statement of what you are trying to do.
To enhance code simplicity and maintainability in C++, minimize the use of arrays and C-style strings Instead, leverage the C++ standard library's string, array, and vector classes, which offer more efficient and modern alternatives to traditional C-style programming Emphasizing the use of these standard library features can lead to cleaner and more robust code.
[9] Avoid pointer arithmetic except in very specialized code (such as a memory manager) and for simple array traversal (e.g., ++p ).
Avoid the misconception that code written in a C style, which excludes C++ features like classes, templates, and exceptions, is inherently more efficient than shorter alternatives that utilize standard-library facilities In many cases, the contrary may actually hold true.
In C, a void pointer can be assigned to or initialized with any pointer type, while in C++, this is not permitted For instance, in the function definition void f(int n), the rules for pointer assignment differ significantly between the two languages.
{ int ∗ p = malloc(n ∗ siz eof(int)); /* not C++; in C++, allocate using ‘‘new’’ */
Dealing with the incompatibility of pointer types can be particularly challenging It's important to understand that implicitly converting a `void*` to a different pointer type is not always safe For example, assigning a `void*` to an `int*` without proper casting can lead to undefined behavior in C++.
∗ pi = 666; // overwr ite ch and other bytes near ch
In both languages, cast the result of malloc() to the right type If you use only C++, avoid malloc()
C and C++ can utilize various linkage conventions due to C++'s stronger focus on type checking and support for function overloading For instance, having multiple global functions named open() necessitates specific adjustments in the linker’s operation to accommodate these differences.
Bibliography
[Boost] The Boost Libraries: free peer-reviewed portable C++ source libraries. www.boost.org.
[C,1990] X3 Secretariat:Standard – The C Language X3J11/90-013 ISO Standard
ISO/IEC 9899-1990 Computer and Business Equipment Manufacturers Association Washington, DC.
[C,1999] ISO/IEC 9899 Standard – The C Language X3J11/90-013-1999.
[C,2011] ISO/IEC 9899 Standard – The C Language X3J11/90-013-2011.
[C++,1998] ISO/IEC JTC1/SC22/WG21 (editor: Andrew Koenig): International Stan- dard – The C++ Language ISO/IEC 14882:1998.
[C++,2004] ISO/IEC JTC1/SC22/WG21 (editor: Lois Goldtwaite):Technical Report on
C++ Performance ISO/IEC TR 18015:2004(E) [C++Math,2010] International Standard – Extensions to the C++ Library to Support Mathe- matical Special Functions ISO/IEC 29124:2010.
[C++,2011] ISO/IEC JTC1/SC22/WG21 (editor: Pete Becker):International Standard –
[C++,2014] ISO/IEC JTC1/SC22/WG21 (editor: Stefanus du Toit): International Stan- dard – The C++ Language ISO/IEC 14882:2014.
[C++,2017] ISO/IEC JTC1/SC22/WG21 (editor: Richard Smith):International Standard
[ConceptsTS] ISO/IEC JTC1/SC22/WG21 (editor: Gabriel Dos Reis):Technical Specifica- tion: C++ Extensions for Concepts ISO/IEC TS 19217:2015.
[CoroutinesTS] ISO/IEC JTC1/SC22/WG21 (editor: Gor Nishanov):Technical Specification:
C++ Extensions for Coroutines ISO/IEC TS 22277:2017.
[Cppreference] Online source for C++ language and standard library facilities. www.cppreference.com.
[Cox,2007] Russ Cox:Regular Expression Matching Can Be Simple And Fast January
2007 swtch.com/˜rsc/regexp/regexp1.html.
[Dahl,1970] O-J Dahl, B Myrhaug, and K Nygaard:SIMULA Common Base Language.
Norwegian Computing Center S-22 Oslo, Norway 1970.
[Dechev,2010] D Dechev, P Pirkelbauer, and B Stroustrup:Understanding and Effectively
Preventing the ABA Problem in Descriptor-based Lock-free Designs 13th
IEEE Computer Society ISORC 2010 Symposium May 2010.
[DosReis,2006] Gabriel Dos Reis and Bjarne Stroustrup: Specifying C++ Concepts.
[Ellis,1989] Margaret A Ellis and Bjarne Stroustrup: The Annotated C++ Reference
Manual Addison-Wesley Reading, Massachusetts 1990 ISBN0-201-51459-1.
[Garcia,2015] J Daniel Garcia and B Stroustrup: Improving performance and maintain- ability through refactoring in C++11 Isocpp.org August 2015. http://www.stroustrup.com/improving_garcia_stroustrup_2015.pdf.
[Garcia,2016] G Dos Reis, J D Garcia, J Lakos, A Meredith, N Myers, B Stroustrup:A
[Garcia,2018] G Dos Reis, J D Garcia, J Lakos, A Meredith, N Myers, B Stroustrup:
Support for contract based programming in C++ P0542R4 2018-4-2. [Friedl,1997] Jeffrey E F Friedl: Mastering Regular Expressions O’Reilly Media.
[GSL] N MacIntosh (Editor):Guidelines Support Library https://github.com/mi- crosoft/gsl.
[Gregor,2006] Douglas Gregor et al.:Concepts: Linguistic Support for Generic Program- ming in C++ OOPSLA’06.
[Hinnant,2018] Howard Hinnant: Date https://howardhinnant.github.io/date/date.html.
[Hinnant,2018b] Howard Hinnant: Timezones https://howardhinnant.github.io/date/tz.html.
[Ichbiah,1979] Jean D Ichbiah et al.: Rationale for the Design of the ADA Pro gramming
Language SIGPLAN Notices Vol 14, No 6 June 1979.
[Kazakova,2015] Anastasia Kazakova:Infographic: C/C++ facts. https://blog.jetbrains.com/clion/2015/07/infographics-cpp-facts-before-clion/ July 2015.
[Kernighan,1978] Brian W Kernighan and Dennis M Ritchie:The C Programming Language.
Prentice Hall Englewood Cliffs, New Jersey 1978.
[Kernighan,1988] Brian W Kernighan and Dennis M Ritchie:The C Programming Language,
Second Edition Prentice-Hall Englewood Cliffs, New Jersey 1988 ISBN 0-13-110362-8.
[Knuth,1968] Donald E Knuth: The Art of Computer Programming Addison-Wesley.
[Koenig,1990] A R Koenig and B Stroustrup: Exception Handling for C++ (revised).
[Maddock,2009] John Maddock:Boost.Regex www.boost.org 2009 2017.
[ModulesTS] ISO/IEC JTC1/SC22/WG21 (editor: Gabriel Dos Reis):Technical Specifica- tion: C++ Extensions for Modules ISO/IEC TS 21544:2018.
[Orwell,1949] George Orwell:1984 Secker and Warburg London 1949.
[Paulson,1996] Larry C Paulson:ML for the Working Programmer Cambridge University
[RangesTS] ISO/IEC JTC1/SC22/WG21 (editor: Eric Niebler):
Technical Specification: C++ Extensions for Ranges ISO/IEC TS 21425:2017 ISBN 0-521-56543-X.
[Richards,1980] Martin Richards and Colin Whitby-Strevens:BCPL – The Language and Its
Compiler Cambridge University Press Cambridge 1980.
[Stepanov,1994] Alexander Stepanov and Meng Lee: The Standard Template Library HP
[Stepanov,2009] Alexander Stepanov and Paul McJones: Elements of Programming Addi- son-Wesley 2009 ISBN 978-0-321-63537-2.
[Stroustrup,1982] B Stroustrup:Classes: An Abstract Data Type Facility for the C Language.
Sigplan Notices January 1982 The first public description of ‘‘C with Classes.’’
[Stroustrup,1984] B Stroustrup: Operator Overloading in C++ Proc IFIP WG2.4 Confer- ence on System Implementation Languages: Experience & Assessment. September 1984.
[Stroustrup,1985] B Stroustrup: An Extensible I/O Facility for C++ Proc Summer 1985
[Stroustrup,1986] B Stroustrup: The C++ Programming Language Addison-Wesley Read- ing, Massachusetts 1986 ISBN 0-201-12078-X.
[Stroustrup,1987] B Stroustrup: Multiple Inheritance for C++ Proc EUUG Spring Confer- ence May 1987.
[Stroustrup,1987b] B Stroustrup and J Shopiro:A Set of C Classes for Co-Routine Style Pro- gramming Proc USENIX C++ Conference Santa Fe, New Mexico No- vember 1987.
[Stroustrup,1988] B Stroustrup:Parameterized Types for C++ Proc USENIX C++ Confer- ence, Denver, Colorado 1988.
[Stroustrup,1991] B Stroustrup: The C++ Programming Language (Second Edition) Addi- son-Wesley Reading, Massachusetts 1991 ISBN 0-201-53992-6.
[Stroustrup,1993] B Stroustrup:A History of C++: 1979–1991 Proc ACM History of Pro- gramming Languages Conference (HOPL-2) ACM Sigplan Notices Vol 28,
[Stroustrup,1994] B Stroustrup:The Design and Evolution of C++ Addison-Wesley Read- ing, Massachusetts 1994 ISBN 0-201-54330-3.
[Stroustrup,1997] B Stroustrup: The C++ Programming Language, Third Edition Addison-
Wesley Reading, Massachusetts 1997 ISBN 0-201-88954-4 Hardcover (‘‘Special’’) Edition 2000 ISBN 0-201-70073-5.
[Stroustrup,2002] B Stroustrup:C and C++: Siblings,C and C++: A Case for Compatibility, andC and C++: Case Studies in Compatibility The C/C++ Users Journal.
July-September 2002 www.stroustrup.com/papers.html.
[Stroustrup,2007] B Stroustrup: Evolving a language in and for the real world: C++
[Stroustrup,2009] B Stroustrup: Programming – Principles and Practice Using C++ Addi- son-Wesley 2009 ISBN 0-321-54372-6.
[Stroustrup,2010] B Stroustrup:The C++11 FAQ www.stroustrup.com/C++11FAQ.html. [Stroustrup,2012a] B Stroustrup and A Sutton:A Concept Design for the STL WG21 Techni- cal Report N3351=-0041 January 2012.
[Stroustrup,2012b] B Stroustrup:Software Development for Infrastructure Computer January
[Stroustrup,2013] B Stroustrup:The C++ Programming Language (Fourth Edition) Addison-
[Stroustrup,2014] B Stroustrup: C++ Applications http://www.stroustrup.com/applica- tions.html.
[Stroustrup,2015] B Stroustrup and H Sutter:C++ Core Guidelines. https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuide- lines.md.
[Stroustrup,2015b] B Stroustrup, H Sutter, and G Dos Reis: A brief introduction to C++’s model for type- and resource-safety Isocpp.org October 2015 Revised
December 2015 http://www.stroustrup.com/resource-model.pdf.
[Sutton,2011] A Sutton and B Stroustrup: Design of Concept Libraries for C++ Proc.
SLE 2011 (International Conference on Software Language Engineering). July 2011.
[WG21] ISO SC22/WG21 The C++ Programming Language Standards Committee:
Document Archive www.open-std.org/jtc1/sc22/wg21.
[Williams,2012] Anthony Williams:C++ Concurrency in Action – Practical Multithreading.
[Woodward,1974] P M Woodward and S G Bond:Algol 68-R Users Guide Her Majesty’s
Advice
[2] When chosing a style for a new project or when modernizing a code base, rely on the C++ Core Guidelines; §16.1.4.
[3] When learning C++, don’t focus on language features in isolation; §16.2.1.
[4] Don’t get stuck with decades-old language-feature sets and design techniques; §16.1.4.
[5] Before using a new feature in production code, try it out by writing small programs to test the standards conformance and performance of the implementations you plan to use.
[6] For learning C++, use the most up-to-date and complete implementation of Standard C++ that you can get access to.
[7] The common subset of C and C++ is not the best initial subset of C++ to learn; §16.3.2.1.
[8] Prefer named casts, such as static_cast over C-style casts; §16.2.7.
[9] When converting a C program to C++, first make sure that function declarations (prototypes) and standard headers are used consistently; §16.3.2.
[10] When converting a C program to C++, rename variables that are C++ keywords; §16.3.2.
[11] For portability and type safety, if you must use C, write in the common subset of C and C++; §16.3.2.1.
[12] When converting a C program to C++, cast the result of malloc() to the proper type or change all uses of malloc() to uses of new ; §16.3.2.2.
[13] When converting from malloc() and free() to new and delete , consider using vector , push_back() , and reser ve() instead of realloc() ; §16.3.2.1.
[14] In C++, there are no implicit conversions from int s to enumerations; use explicit type conver- sion where necessary.
[15] For each standard C header that places names in the global namespace, the header places the names in namespace std
[16] Use extern "C" when declaring C functions; §16.3.2.3.
[17] Prefer string over C-style strings (direct manipulation of zero-terminated arrays of char ).
[19] Prefer containers (e.g., vector ) over built-in arrays.
Knowledge is of two kinds.
We know a subject ourselves, or we know where we can find information on it.
& address-of operator 11 reference to 12
∗ contents-of operator 11 multiply operator 6 pointer to 11 regex 117
∗ ? lazy 118 + plus operator 6 regex 117 str ing concatenation 111 ++, increment operator 7 += operator 7 str ing append 112 +? lazy 118
-, minus operator 6 , decrement operator 7 , regex 117
/, divide operator 6 // comment 2 /=, scaling operator 7 : public 55
= container 147 greater-than-or-equal operator 6
[] array 171 array of 11 str ing 112
A abs() 188 abstract class 54 type 54 accumulate() 189 acquisition RAII, resource 164 adaptor, lambda as 180 address, memory 16 address-of operator & 11 adjacent_difference() 189 aims, C++11 213 algorithm 149 container 150, 160 lifting 100 numerical 189 parallel 161 standard library 156
The article discusses various C++ programming concepts, including the use of algorithms, templates, and memory allocation techniques Key topics include the differences between arrays and vectors, the significance of ANSI C++ standards, and the use of regular expressions such as alnum and alpha for character classification It also covers essential functions like data(), size(), and the importance of constrained templates and default functions in type handling Understanding these elements is crucial for effective coding and optimization in C++.
109 asin() 188 assembler 210 asser t() 40 assertion static_asser t 40 Assignable, concept 158 assignment
=, str ing 112 copy 66, 69 initialization and 18 move 66, 72 associative array – see map
– A – Index 229 async() launch 204 at() 141 atan() 188 atan2() 188
AT&T Bell Laboratories 212 auto = 8 auto_ptr, deprecated 218
B back_inser ter() 150 backslash \ 3 bad_var iant_access 176 base and derived class 55 basic_str ing 114
BCPL 219 begin() 75, 143, 147, 150 beginner, book for 1
Bell Laboratories, AT&T 212 beta() 188 bibliography 222
The article discusses several programming concepts, including Bidirectional Range and binary search, highlighting their significance in structured data management It explores the use of bit-fields and bitsets, along with their interaction with enums, to optimize memory usage Additionally, it addresses the implementation of blocks as function bodies and the importance of the 'try' statement in error handling The content is aimed at beginners, providing foundational knowledge in programming with a focus on boolean operations and efficient coding practices.
K&R 219 void ∗ assignment, difference from 221 with Classes 208 with Classes language features 210 with Classes standard library 211
The evolution of the C++ programming language is marked by several key standards, including C++98, C++03, C++11, C++14, C++17, and C++20 Each version introduced significant language features and library components, with C++11 focusing on modern programming enhancements and C++14 refining those features further C++17 continued this trend, while C++20 introduced concepts, contracts, and modules, reflecting a commitment to standardization and modern development practices The ISO standards play a crucial role in this timeline, ensuring consistency and clarity in the language's evolution Understanding these core guidelines and their historical context is essential for developers aiming to leverage C++ effectively.
The C89 and C99 standards, along with the 218 call operator, are essential in understanding programming nuances such as callbacks, capacity functions, and exception handling Key concepts include the capture list in lambda expressions, the significance of the catch clause for every exception, and the use of catch( ) for generic exception handling Additionally, understanding character sets, compile-time and run-time checks, and the cost of range checking is crucial The chrono namespace further enriches the programming landscape, providing tools for time manipulation and measurement.
109, 179, 200 class 48 abstract 54 base and derived 55 concrete 48 hierarchy 57 scope 9 template 79
109, 188 cntr l, regex 119 code complexity, function and 4 comment, // 2
CommonReference, concept 158 common_type_t 158 communication, task 202 comparison 74 operator 6, 74 compatibility, C and C++ 218 compilation model, template 104 separate 30 compiler 2 compile-time check 40 computation 181 evaluation 10 complete encapsulation 66 complex 49, 190
109, 188, 190 complexity, function and code 4 components
C++17 library 217 computation, compile-time 181 concatenation +, str ing 111 concept 81, 94
Mergeable 159 Movable 158 MoveConstr uctible 158 OutputIterator 159 OutputRange 160 Permutable 159 Predicate 159 RandomAccessIterator 159 RandomAccessRange 160 Range 157
Range 160 Regular 158 Relation 159 Same 158 Semiregular 158 Sentinel 159 SignedIntegral 158 SizedRange 160 SizedSentinel 159 Sor table 159 Str ictTotallyOrdered 158 Str ictWeakOrder 159 support 94
C++20 94 definition of 97 in 158 in 158 in 158
, concepts in 158 concrete class 48 type 48 concurrency 195 condition, declaration in 61 condition_var iable 201 notify_one() 202 wait() 201
201 const immutability 9 member function 50 constant expression 10 const_cast 53 constexpr function 10 immutability 9
– C – Index 231 const_iterator 154 constrained argument 81 template 82 template argument 82
Constr uctible, concept 158 constructor and destructor 210 copy 66, 69 default 50 delegating 215 explicit 67 inheriting 216 initializer-list 52 invariant and 37 move 66, 71 container 51, 79, 137
!= 147 algorithm 150, 160 allocator new 178 almost 170 object in 140 overview 146 retur n 151 sor t() 181 specialized 170 standard library 146 contents-of operator ∗ 11 contract 40 contracts, C++20 40 conversion 67 explicit type 53 narrowing 8 conversions, usual arithmetic 7
ConvertibleTo, concept 158 copy 68 assignment 66, 69 constructor 66, 69 cost of 70 elision 72 elision 66 memberwise 66 copy() 156
CopyConstr uctible, concept 158 copy_if() 156
Core Guidelines, C++ 214 core language, C++ 2 coroutine 211 cos() 188 cosh() 188 cost of copy 70 of range checking 142 count() 156 count_if() 155–156 cout, output 3
110 C-style error handling 188 string 13
In programming, understanding key concepts such as data races, deadlocks, and debugging templates is essential for effective software development The use of declarations, including function and member initializers, plays a critical role in defining operations within a program Concepts like DefaultConstructible and DerivedFrom are crucial for managing object creation and inheritance in C++ Additionally, utilizing operators such as the decrement operator and delete operator requires careful consideration to avoid deprecated features Mastery of these elements, including the implementation of concepts and template arguments, enhances code efficiency and maintainability.
Destr uctible, concept 158 destructor 51, 66 ˜ 51 constructor and 210 vir tual 59 dictionary – see map difference from C 218 from C void ∗ assignment 221 digit, [[:digit:]] 119 digit, regex 119
-directive, using 35 dispatch, tag 181 distribution, random 191 divide operator / 6 domain error 188 double 5 duck typing 104 duration 179 duration_cast 179 dynamic store 51 dynamic_cast 61 is instance of 62 is kind of 62
EDOM 188 element requirements 140 elision, copy 66 emplace_back() 147 empty() 147 enable_if 184 encapsulation, complete 66 end() 75, 143, 147, 150 engine, random 191 enum, bitset and 172 equal operator == 6 equality preserving 159
The article discusses various programming concepts, including the handling of errors with specific functions like erase() and find_all(), as well as the distinction between error codes and exceptions It highlights essential operations such as evaluation at compile-time and run-time, alongside execution policies and explicit type conversions The text also references specific functions like exclusive_scan() and exponential_distribution(), and mentions the use of lambda expressions and external templates in programming.
F fabs() 188 facilities, standard library 108 fail_fast 170 feature, deprecated 218 features
The article discusses various programming languages and concepts, including C with Classes (210), C++11 (215), C++14 (216), and C++17 (216) It highlights key functions such as find() (150, 156), find_all() (example 151), and find_if() (155-156), as well as the first pair member (173) and mathematical functions like floor() and fmod() (188) Additionally, it covers the for statement (11), range statements (11), and advanced topics like forward() (167) and perfect forwarding (168) The article also introduces ForwardIterator as a concept (159) and discusses forward_list, a type of singly-linked list (143).
109 ForwardRange, concept 160 free store 51 frexp() 188
In the realm of programming, understanding function complexity is crucial, with particular emphasis on aspects such as argument passing, default arguments, and return values The implementation of virtual functions and the use of templates play a significant role in enhancing code efficiency Additionally, concepts like `constexpr` and the handling of fundamental types contribute to better performance Utilizing `try` blocks and managing const members are essential for robust error handling Moreover, the interplay between futures and promises is vital for asynchronous programming, exemplified by member functions like `get()`, which facilitate effective value retrieval.
G garbage collection 73 generic programming 93, 210 get() by index 174 by type 174 get(), future member 202 graph, regex 119 greater-than operator > 6 greater-than-or-equal operator >= 6 greedy match 118, 121 grouping, {} 2 gsl namespace 168 span 168
H half-open sequence 156 handle 52 resource 69, 165 hardware, mapping to 16 hash table 144 hash, unordered_map 76 header
C-library 110 file 31 standard library 109 heap 51
Hello, Wor ld! example 2 hierarchy class 57 navigation 61 history, C++ 207 HOPL 208
I if statement 14 immutability const 9 constexpr 9 implementation definition 30 inheritance 60 iterator 153 of vir tual function 56 str ing 113 in-class member initialization 215
#include 31 inclusive_scan() 189 increment operator ++ 7 index, get() by 174 inheritance 55 implementation 60 interface 60 multiple 211 inheriting constructor 216 initialization and assignment 18 in-class member 215 initialize 52 array 171 initializer
This article discusses various programming concepts and features, including the use of default member initializers, initializer-list constructors, and the importance of inline functions within namespaces It also covers InputIterator and InputRange concepts, along with the insert() function and instantiation processes Additionally, the article highlights machine instructions, integral concepts, interface declarations, inheritance, and invariants related to constructors.
3, 109 iota() 189 is instance of, dynamic_cast 62 kind of, dynamic_cast 62
ISO-14882 212 istream_iterator 154 iterator 75, 150 and I/O 154 implementation 153
182 concepts in 158 iterator_categor y 182 iterator_traits 181–182 iterator_type 182
\l, regex 119 lambda as adaptor 180 expression 87 language and library 107 features, C with Classes 210 features, C++11 215 features, C++14 216 features, C++17 216 launch, async() 204 lazy
∗? 118 match 118, 121 ldexp() 188 leak, resource 62, 72, 164 less-than operator < 6 less-than-or-equal operator > 215 class 79 compilation model 104 constrained 82 debugging 100 exter n 215 function 84 purpose 93 requirement 94 variadic 100 this 70 thread join() 196 packaged_task 203 task and 196
In C++, the concept of thread-local storage is essential for managing concurrent programming, allowing for unique data per thread The timing mechanisms, such as time_point and clock, facilitate precise time measurement and performance tracking Understanding translation units and the try block is crucial for error handling and resource management through RAII (Resource Acquisition Is Initialization) Additionally, structured bindings and tuples enhance data management, while type conversions and polymorphism provide flexibility in function arguments The use of explicit functions ensures clarity in type conversions, and the size of types can be managed effectively with typename.
\u, regex 119 udl 75 Unicode string 114 unifor m_int_distribution 191 uninitialized 8 unique_copy() 149, 156 unique_lock 200–201 and scoped_lock 201 unique_ptr 62, 164 unordered_map 144, 146 hash 76 map and 146
109 unordered_multimap 146 unordered_multiset 146 unordered_set 146 unsigned 5UnsignedIntegral, concept 158 upper, regex 119 use, concept 94 user-defined literal 75, 215 operator 51 using alias 90
192 value 5 argument 82 key and 144 mapped type 144 return, function 66 valuetype 147 value_type 90 variable 5 variadic template 100 variant 175
Vec example 141 range checking 140 vector arithmetic 192 vector 138, 146 array vs 171
View, concept 160 vir tual 54 destructor 59 function, implementation of 56 function table vtbl 56 pure 54 void ∗ 221
∗ assignment, difference from C 221 retur n type 3 vtbl, vir tual function table 56
\W, regex 119 wait(), condition_var iable 201
This page intentionally left blank