Preface
Who this book is for
What this book covers
To get the most out of this book
Download the example code files
Conventions used
Get in touch
Reviews
A Brief Introduction to C++
Why C++?
Zero-cost abstractions
Programming languages and machine code abstractions
Abstractions in other languages
Portability
Robustness
C++ of today
The aim of this book
Expected knowledge of the reader
C++ compared with other languages
Competing languages and performance
Non-performance-related C++ language features
Value semantics
Const correctness
Object ownership and garbage collection in C++
Avoiding null objects using C++ references
Drawbacks of C++
Class interfaces and exceptions
Strict class interfaces
Error handling and resource acquisition
Preserving the valid state
Resource acquisition
Exceptions versus error codes
Libraries used in this book
Summary
Modern C++ Concepts
Automatic type deduction with the auto keyword
Using auto in function signatures
Using auto for variables
Const reference
Mutable reference
Forwarding reference
Conclusion
The lambda function
Basic syntax of a C++ lambda function
The capture block
Capture by reference versus capture by value
Similarities between a Lambda and a class
Initializing variables in capture
Mutating lambda member variables
Mutating member variables from the compiler's perspective
Capture all
Assigning C function pointers to lambdas
Lambdas and std::function
Assigning lambdas to std::functions
Implementing a simple Button class with std::function
Performance consideration of std::function
An std::function cannot be inlined
An std::function heap allocates and captures variables
Invoking an std::function requires a few more operations than a lambda
The polymorphic lambda
Creating reusable polymorphic lambdas
Const propagation for pointers
Move semantics explained
Copy-construction, swap, and move
Copy-constructing an object
Swapping two objects
Move-constructing an object
Resource acquisition and the rule of three
Implementing the rule of three
Constructor
Limitations of the rule of three
Avoiding copies without move semantics
Introducing move semantics
Named variables and r-values
Accept arguments by move when applicable
Default move semantics and the rule of zero
Rule of zero in a real code base
A note on empty destructors
A common pitfall - moving non-resources
Applying the && modifier to class member functions
Representing optional values with std::optional
Optional return values
Optional member variables
Sorting and comparing std::optional
Representing dynamic values with std::any
Performance of std::any
Summary
Measuring Performance
Asymptotic complexity and big O notation
Growth rates
Amortized time complexity
What to measure?
Performance properties
Performance testing – best practices
Knowing your code and hot spots
Profilers
Instrumentation profilers
Sampling profilers
Summary
Data Structures
Properties of computer memory
STL containers
Sequence containers
Vector and array
Deque
List and forward_list
The basic_string
Associative containers
Ordered sets and maps
Unordered sets and maps
Hash and equals
Hash policy
Container adaptors
Priority queues
Parallel arrays
Summary
A Deeper Look at Iterators
The iterator concept
Iterator categories
Pointer-mimicking syntax
Iterators as generators
Iterator traits
Implementing a function using iterator categories
Extending the IntIterator to bidirectional
Practical example – iterating floating point values within a range
Illustrated usage examples
Utility functions
How to construct a linear range iterator
Iterator usage example
Generalizing the iterator pair to a range
The make_linear_range convenience function
Linear range usage examples
Summary
STL Algorithms and Beyond
Using STL algorithms as building blocks
STL algorithm concepts
Algorithms operate on iterators
Implementing a generic algorithm that can be used with any container
Iterators for a range point to the first element and the element after the last
Algorithms do not change the size of the container
Algorithms with output require allocated data
Algorithms use operator== and operator< by default
Custom comparator function
General-purpose predicates
Algorithms require move operators not to throw
Algorithms have complexity guarantees
Algorithms perform just as well as C library function equivalents
STL algorithms versus handcrafted for-loops
Readability and future-proofing
Real-world code base example
Usage examples of STL algorithms versus handcrafted for-loops
Example 1 – Unfortunate exceptions and performance problems
Example 2 – STL has subtle optimizations even in simple algorithms
Sorting only for the data you need to retrieve
Use cases
Performance evaluation
The future of STL and the ranges library
Limitations of the iterators in STL
Introduction to the ranges library
Composability and pipeability
Actions, views, and algorithms
Actions
Views
Algorithms
Summary
Memory Management
Computer memory
The virtual address space
Memory pages
Thrashing
Process memory
Stack memory
Heap memory
Objects in memory
Creating and deleting objects
Placement new
The new and delete operators
Memory alignment
Padding
Memory ownership
Handling resources implicitly
Containers
Smart pointers
Unique pointer
Shared pointer
Weak pointer
Small size optimization
Custom memory management
Building an arena
A custom memory allocator
Summary
Metaprogramming and Compile-Time Evaluation
Introduction to template metaprogramming
Using integers as template parameters
How the compiler handles a template function
Using static_assert to trigger errors at compile time
Type traits
Type trait categories
Using type traits
Receiving the type of a variable with decltype
Conditionally enable functions based on types with std::enable_if_t
Introspecting class members with std::is_detected
Usage example of is_detected and enable_if_t combined
The constexpr keyword
Constexpr functions in a runtime context
Verify compile-time computation using std::integral_constant
The if constexpr statement
Comparison with runtime polymorphism
Example of generic modulus function using if constexpr
Heterogeneous containers
Static-sized heterogenous containers
The std::tuple container
Accessing the members of a tuple
Iterating std::tuple
Unrolling the tuple
Implementing other algorithms for tuples
Accessing tuple elements
Structured bindings
The variadic template parameter pack
An example of a function with variadic number of arguments
How to construct a variadic parameter pack
Dynamic-sized heterogenous containers
Using std::any as the base for a dynamic-size heterogenous container
The std::variant
Visiting variants
Heterogenous container of variants
Accessing the values in our variant container
Global function std::get
Real world examples of metaprogramming
Example 1 – Reflection
Making a class reflect its members
C++ libraries which simplifies reflection
Using the reflection
Evaluating the assembler output of the reflection
Conditionally overloading global functions
Testing reflection capabilities
Example 2 – Creating a generic safe cast function
Example 3 – Hash strings at compile time
The advantages of compile-time hash sum calculation
Implement and verify a compile-time hash function
Constructing a PrehashedString class
Forcing PrehashedString to only accept compile time string literals
Evaluating PrehashedString
Evaluating get_bitmap_resource() with PrehashedString
Summary
Proxy Objects and Lazy Evaluation
An introduction to lazy evaluation and proxy objects
Lazy versus eager evaluation
Proxy objects
Comparing concatenated strings using a proxy
Implementing the proxy
Performance evaluation
The r-value modifier
Assigning a concatenated proxy
Postponing an sqrt computation when comparing distances
A simple two-dimensional point class
The underlying mathematics
Implementing the DistProxy object
Expanding DistProxy to something more useful
Comparing distances with DistProxy
Calculating distances with DistProxy
Preventing the misuse of DistProxy
Performance evaluation
Creative operator overloading and proxy objects
The pipe operator as an extension method
The pipe operator
The infix operator
Further reading
Summary
Concurrency
Understanding the basics of concurrency
What makes concurrent programming hard?
Concurrency and parallelism
Time slicing
Shared memory
Data races
Mutex
Deadlock
Synchronous and asynchronous tasks
Concurrent programming in C++
The thread support library
Threads
Thread states
Protecting critical sections
Avoiding deadlocks
Condition variables
Returning data and handling errors
Tasks
Atomic support in C++
Using shared_ptr in a multithreaded environment
C++ memory model
Instruction reordering
Atomics and memory orders
Lock-free programming
Lock-free queue example
Performance guidelines
Avoid contention
Avoid blocking operations
Number of threads/CPU cores
Thread priorities
Thread affinity
False sharing
Summary
Parallel STL
Importance of parallelism
Parallel algorithms
Implementing parallel std::transform()
Naive implementation
Performance evaluation
Shortcomings of the naive implementation
Divide and conquer
Implementation
Performance evaluation
Implementing parallel std::count_if
Implementing parallel std::copy_if
Approach one – Use a synchronized write position
Inner function
Outer function
Approach two – Split algorithm into two parts
Part one – Copy elements in parallel into the destination range
Part two – Move the sparse range sequentially into a continuous range
Performance evaluation
Parallel STL
Execution policies
Sequenced policy
Parallel policy
Parallel unsequenced policy
Parallel modifications of algorithm
std::accumulate and std::reduce
std::transform_reduce
std::for_each
Parallelizing an index-based for-loop
Combining std::for_each with linear range
Simplifying construction via a wrapper
Executing STL algorithms on the GPU
GPU APIs and parallel operations
Programmable GPUs
Shader programs
STL algorithms and the GPU
Boost Compute
Basic concepts of Boost Compute
OpenCL
Initializing Boost Compute
Transfer a simple transform-reduce algorithm to Boost Compute
The algorithm in standard C++
Transforming the algorithm to Boost Compute
Adapting the circle struct for use with Boost Compute
Converting circle_area_cpu to Boost Compute
The BOOST_COMPUTE_FUNCTION macro
Implementing the transform-reduction algorithm on the GPU
Using predicates with Boost Compute
Using a custom kernel in Boost Compute
Box filter
Implementing the kernel
Parallelizing for two dimensions
Verify GPU computation on the CPU
Summary
Other Books You May Enjoy
Leave a review - let other readers know what you think