100% found this document useful (1 vote)
227 views610 pages

CppProfessionalTipsSecrets PDF

Uploaded by

mrsoslavica
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (1 vote)
227 views610 pages

CppProfessionalTipsSecrets PDF

Uploaded by

mrsoslavica
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 610

C++

Complete

C++
Complete Tips & Secrets for Professionals

Tips & Secrets


for Professionals

500+ pages
of professional hints and tricks

Disclaimer
GoalKicker.com This is an unocial free book created for educational purposes and is
not aliated with ocial C++ group(s) or company(s).
Free Programming Books All trademarks and registered trademarks are
the property of their respective owners
Contents
About ................................................................................................................................................................................... 1
Chapter 1: Getting started with C++ .................................................................................................................... 2
Section 1.1: Hello World ................................................................................................................................................. 2
Section 1.2: Comments .................................................................................................................................................. 3
Section 1.3: The standard C++ compilation process .................................................................................................. 5
Section 1.4: Function ...................................................................................................................................................... 5
Section 1.5: Visibility of function prototypes and declarations ................................................................................. 8
Section 1.6: Preprocessor .............................................................................................................................................. 9
Chapter 2: Templates ............................................................................................................................................... 10
Section 2.1: Basic Class Template .............................................................................................................................. 10
Section 2.2: Function Templates ................................................................................................................................ 11
Section 2.3: Variadic template data structures ........................................................................................................ 12
Section 2.4: Argument forwarding ............................................................................................................................ 15
Section 2.5: Partial template specialization .............................................................................................................. 15
Section 2.6: Template Specialization ......................................................................................................................... 17
Section 2.7: Alias template ......................................................................................................................................... 17
Section 2.8: Explicit instantiation ................................................................................................................................ 17
Section 2.9: Non-type template parameter ............................................................................................................. 18
Section 2.10: Declaring non-type template arguments with auto ......................................................................... 19
Section 2.11: Template template parameters ........................................................................................................... 20
Section 2.12: Default template parameter value ..................................................................................................... 20
Chapter 3: Metaprogramming ............................................................................................................................. 21
Section 3.1: Calculating Factorials ............................................................................................................................. 21
Section 3.2: Iterating over a parameter pack .......................................................................................................... 23
Section 3.3: Iterating with std::integer_sequence .................................................................................................... 24
Section 3.4: Tag Dispatching ...................................................................................................................................... 25
Section 3.5: Detect Whether Expression is Valid ...................................................................................................... 26
Section 3.6: If-then-else .............................................................................................................................................. 27
Section 3.7: Manual distinction of types when given any type T ........................................................................... 27
Section 3.8: Calculating power with C++11 (and higher) .......................................................................................... 28
Section 3.9: Generic Min/Max with variable argument count ................................................................................. 29
Chapter 4: Iterators .................................................................................................................................................. 29
Section 4.1: Overview ................................................................................................................................................... 29
Section 4.2: Vector Iterator ........................................................................................................................................ 32
Section 4.3: Map Iterator ............................................................................................................................................ 32
Section 4.4: Reverse Iterators .................................................................................................................................... 33
Section 4.5: Stream Iterators ...................................................................................................................................... 34
Section 4.6: C Iterators (Pointers) .............................................................................................................................. 34
Section 4.7: Write your own generator-backed iterator ......................................................................................... 35
Chapter 5: Returning several values from a function ............................................................................. 36
Section 5.1: Using std::tuple ......................................................................................................................................... 36
Section 5.2: Structured Bindings ................................................................................................................................ 37
Section 5.3: Using struct ............................................................................................................................................. 39
Section 5.4: Using Output Parameters ...................................................................................................................... 40
Section 5.5: Using a Function Object Consumer ...................................................................................................... 40
Section 5.6: Using std::pair .......................................................................................................................................... 41
Section 5.7: Using std::array ....................................................................................................................................... 41
Section 5.8: Using Output Iterator ............................................................................................................................. 41
Section 5.9: Using std::vector ..................................................................................................................................... 42
Chapter 6: std::string ................................................................................................................................................ 42
Section 6.1: Tokenize ................................................................................................................................................... 43
Section 6.2: Conversion to (const) char* ................................................................................................................... 44
Section 6.3: Using the std::string_view class ............................................................................................................ 45
Section 6.4: Conversion to std::wstring ...................................................................................................................... 45
Section 6.5: Lexicographical comparison ................................................................................................................. 46
Section 6.6: Trimming characters at start/end ........................................................................................................ 48
Section 6.7: String replacement ................................................................................................................................. 49
Section 6.8: Converting to std::string ......................................................................................................................... 50
Section 6.9: Splitting .................................................................................................................................................... 50
Section 6.10: Accessing a character .......................................................................................................................... 51
Section 6.11: Checking if a string is a prefix of another ........................................................................................... 51
Section 6.12: Looping through each character ......................................................................................................... 52
Section 6.13: Conversion to integers/floating point types ...................................................................................... 52
Section 6.14: Concatenation ....................................................................................................................................... 53
Section 6.15: Converting between character encodings ......................................................................................... 53
Section 6.16: Finding character(s) in a string ............................................................................................................ 54
Chapter 7: Namespaces .......................................................................................................................................... 55
Section 7.1: What are namespaces? .......................................................................................................................... 55
Section 7.2: Argument Dependent Lookup ............................................................................................................... 56
Section 7.3: Extending namespaces .......................................................................................................................... 57
Section 7.4: Using directive ......................................................................................................................................... 57
Section 7.5: Making namespaces .............................................................................................................................. 58
Section 7.6: Unnamed/anonymous namespaces ................................................................................................... 59
Section 7.7: Compact nested namespaces ............................................................................................................... 59
Section 7.8: Namespace alias .................................................................................................................................... 60
Section 7.9: Inline namespace .................................................................................................................................... 60
Section 7.10: Aliasing a long namespace .................................................................................................................. 62
Section 7.11: Alias Declaration scope ......................................................................................................................... 62
Chapter 8: File I/O ...................................................................................................................................................... 63
Section 8.1: Writing to a file ........................................................................................................................................ 63
Section 8.2: Opening a file .......................................................................................................................................... 64
Section 8.3: Reading from a file ................................................................................................................................. 65
Section 8.4: Opening modes ...................................................................................................................................... 66
Section 8.5: Reading an ASCII file into a std::string .................................................................................................. 67
Section 8.6: Writing files with non-standard locale settings ................................................................................... 68
Section 8.7: Checking end of file inside a loop condition, bad practice? ............................................................... 69
Section 8.8: Flushing a stream ................................................................................................................................... 69
Section 8.9: Reading a file into a container .............................................................................................................. 70
Section 8.10: Copying a file ......................................................................................................................................... 71
Section 8.11: Closing a file ........................................................................................................................................... 71
Section 8.12: Reading a `struct` from a formatted text file. ..................................................................................... 72
Chapter 9: Classes/Structures ............................................................................................................................. 73
Section 9.1: Class basics .............................................................................................................................................. 73
Section 9.2: Final classes and structs ........................................................................................................................ 74
Section 9.3: Access specifiers ..................................................................................................................................... 74
Section 9.4: Inheritance ............................................................................................................................................... 75
Section 9.5: Friendship ................................................................................................................................................ 77
Section 9.6: Virtual Inheritance .................................................................................................................................. 78
Section 9.7: Private inheritance: restricting base class interface ........................................................................... 79
Section 9.8: Accessing class members ..................................................................................................................... 79
Section 9.9: Member Types and Aliases ................................................................................................................... 81
Section 9.10: Nested Classes/Structures ................................................................................................................... 84
Section 9.11: Unnamed struct/class ........................................................................................................................... 88
Section 9.12: Static class members ............................................................................................................................ 89
Section 9.13: Multiple Inheritance ............................................................................................................................... 94
Section 9.14: Non-static member functions .............................................................................................................. 95
Chapter 10: Smart Pointers ................................................................................................................................... 96
Section 10.1: Unique ownership (std::unique_ptr) ..................................................................................................... 96
Section 10.2: Sharing ownership (std::shared_ptr) .................................................................................................. 98
Section 10.3: Sharing with temporary ownership (std::weak_ptr) ........................................................................ 100
Section 10.4: Using custom deleters to create a wrapper to a C interface ......................................................... 101
Section 10.5: Unique ownership without move semantics (auto_ptr) ................................................................. 102
Section 10.6: Casting std::shared_ptr pointers ....................................................................................................... 104
Section 10.7: Writing a smart pointer: value_ptr .................................................................................................... 104
Section 10.8: Getting a shared_ptr referring to this .............................................................................................. 106
Chapter 11: Function Overloading .................................................................................................................... 107
Section 11.1: What is Function Overloading? ........................................................................................................... 107
Section 11.2: Return Type in Function Overloading ................................................................................................ 108
Section 11.3: Member Function cv-qualifier Overloading ...................................................................................... 109
Chapter 12: std::vector ........................................................................................................................................... 110
Section 12.1: Accessing Elements ............................................................................................................................. 110
Section 12.2: Initializing a std::vector ....................................................................................................................... 113
Section 12.3: Deleting Elements ............................................................................................................................... 114
Section 12.4: Iterating Over std::vector .................................................................................................................... 116
Section 12.5: vector<bool>: The Exception To So Many, So Many Rules ............................................................. 118
Section 12.6: Inserting Elements ............................................................................................................................... 119
Section 12.7: Using std::vector as a C array ............................................................................................................ 120
Section 12.8: Finding an Element in std::vector ...................................................................................................... 120
Section 12.9: Concatenating Vectors ....................................................................................................................... 121
Section 12.10: Matrices Using Vectors ..................................................................................................................... 122
Section 12.11: Using a Sorted Vector for Fast Element Lookup ............................................................................. 123
Section 12.12: Reducing the Capacity of a Vector .................................................................................................. 123
Section 12.13: Vector size and capacity ................................................................................................................... 124
Section 12.14: Iterator/Pointer Invalidation ............................................................................................................. 125
Section 12.15: Find max and min Element and Respective Index in a Vector ..................................................... 126
Section 12.16: Converting an array to std::vector ................................................................................................... 127
Section 12.17: Functions Returning Large Vectors ................................................................................................. 127
Chapter 13: Operator Overloading .................................................................................................................. 128
Section 13.1: Arithmetic operators ............................................................................................................................ 129
Section 13.2: Array subscript operator .................................................................................................................... 130
Section 13.3: Conversion operators ......................................................................................................................... 131
Section 13.4: Complex Numbers Revisited .............................................................................................................. 131
Section 13.5: Named operators ................................................................................................................................ 135
Section 13.6: Unary operators .................................................................................................................................. 137
Section 13.7: Comparison operators ........................................................................................................................ 138
Section 13.8: Assignment operator .......................................................................................................................... 139
Section 13.9: Function call operator ......................................................................................................................... 140
Section 13.10: Bitwise NOT operator ........................................................................................................................ 140
Section 13.11: Bit shift operators for I/O .................................................................................................................. 141
Chapter 14: Lambdas ............................................................................................................................................. 141
Section 14.1: What is a lambda expression? ........................................................................................................... 142
Section 14.2: Specifying the return type .................................................................................................................. 144
Section 14.3: Capture by value ................................................................................................................................. 145
Section 14.4: Recursive lambdas .............................................................................................................................. 146
Section 14.5: Default capture ................................................................................................................................... 147
Section 14.6: Class lambdas and capture of this .................................................................................................... 148
Section 14.7: Capture by reference .......................................................................................................................... 149
Section 14.8: Generic lambdas ................................................................................................................................. 150
Section 14.9: Using lambdas for inline parameter pack unpacking .................................................................... 151
Section 14.10: Generalized capture .......................................................................................................................... 152
Section 14.11: Conversion to function pointer .......................................................................................................... 153
Section 14.12: Porting lambda functions to C++03 using functors ....................................................................... 154
Chapter 15: Loops ..................................................................................................................................................... 155
Section 15.1: Range-Based For ................................................................................................................................. 155
Section 15.2: For loop ................................................................................................................................................ 158
Section 15.3: While loop ............................................................................................................................................ 159
Section 15.4: Do-while loop ....................................................................................................................................... 160
Section 15.5: Loop Control statements : Break and Continue ............................................................................... 161
Section 15.6: Declaration of variables in conditions .............................................................................................. 162
Section 15.7: Range-for over a sub-range .............................................................................................................. 162
Chapter 16: std::map ............................................................................................................................................... 163
Section 16.1: Accessing elements ............................................................................................................................. 163
Section 16.2: Inserting elements ............................................................................................................................... 164
Section 16.3: Searching in std::map or in std::multimap ......................................................................................... 166
Section 16.4: Initializing a std::map or std::multimap .............................................................................................. 167
Section 16.5: Checking number of elements ........................................................................................................... 167
Section 16.6: Types of Maps ..................................................................................................................................... 168
Section 16.7: Deleting elements ................................................................................................................................ 168
Section 16.8: Iterating over std::map or std::multimap ........................................................................................... 169
Section 16.9: Creating std::map with user-defined types as key .......................................................................... 170
Chapter 17: Threading ............................................................................................................................................ 170
Section 17.1: Creating a std::thread .......................................................................................................................... 171
Section 17.2: Passing a reference to a thread ........................................................................................................ 172
Section 17.3: Using std::async instead of std::thread .............................................................................................. 173
Section 17.4: Basic Synchronization ......................................................................................................................... 174
Section 17.5: Create a simple thread pool .............................................................................................................. 174
Section 17.6: Ensuring a thread is always joined .................................................................................................... 176
Section 17.7: Operations on the current thread ...................................................................................................... 177
Section 17.8: Using Condition Variables .................................................................................................................. 178
Section 17.9: Thread operations ............................................................................................................................... 179
Section 17.10: Thread-local storage ......................................................................................................................... 180
Section 17.11: Reassigning thread objects ............................................................................................................... 180
Chapter 18: Value Categories ............................................................................................................................. 181
Section 18.1: Value Category Meanings .................................................................................................................. 181
Section 18.2: rvalue .................................................................................................................................................... 181
Section 18.3: xvalue ................................................................................................................................................... 182
Section 18.4: prvalue ................................................................................................................................................. 182
Section 18.5: lvalue .................................................................................................................................................... 183
Section 18.6: glvalue .................................................................................................................................................. 183
Chapter 19: Preprocessor ..................................................................................................................................... 183
Section 19.1: Include Guards ..................................................................................................................................... 184
Section 19.2: Conditional logic and cross-platform handling ............................................................................... 184
Section 19.3: X-macros .............................................................................................................................................. 186
Section 19.4: Macros .................................................................................................................................................. 187
Section 19.5: Predefined macros .............................................................................................................................. 190
Section 19.6: Preprocessor Operators ..................................................................................................................... 192
Section 19.7: #pragma once ..................................................................................................................................... 193
Section 19.8: Preprocessor error messages ........................................................................................................... 193
Chapter 20: SFINAE (Substitution Failure Is Not An Error) .................................................................. 193
Section 20.1: What is SFINAE .................................................................................................................................... 193
Section 20.2: void_t .................................................................................................................................................. 194
Section 20.3: enable_if ............................................................................................................................................. 195
Section 20.4: is_detected ......................................................................................................................................... 197
Section 20.5: Overload resolution with a large number of options ..................................................................... 198
Section 20.6: trailing decltype in function templates ............................................................................................ 199
Section 20.7: enable_if_all / enable_if_any ......................................................................................................... 200
Chapter 21: The Rule of Three, Five, And Zero .......................................................................................... 202
Section 21.1: Rule of Zero .......................................................................................................................................... 202
Section 21.2: Rule of Five .......................................................................................................................................... 203
Section 21.3: Rule of Three ....................................................................................................................................... 204
Section 21.4: Self-assignment Protection ................................................................................................................ 205
Chapter 22: RAII: Resource Acquisition Is Initialization ......................................................................... 206
Section 22.1: Locking ................................................................................................................................................. 206
Section 22.2: ScopeSuccess (c++17) ........................................................................................................................ 207
Section 22.3: ScopeFail (c++17) ................................................................................................................................ 208
Section 22.4: Finally/ScopeExit ................................................................................................................................ 209
Chapter 23: Exceptions .......................................................................................................................................... 210
Section 23.1: Catching exceptions ............................................................................................................................ 210
Section 23.2: Rethrow (propagate) exception ....................................................................................................... 211
Section 23.3: Best practice: throw by value, catch by const reference ............................................................... 212
Section 23.4: Custom exception ............................................................................................................................... 213
Section 23.5: std::uncaught_exceptions .................................................................................................................. 215
Section 23.6: Function Try Block for regular function ........................................................................................... 216
Section 23.7: Nested exception ................................................................................................................................ 216
Section 23.8: Function Try Blocks In constructor ................................................................................................... 218
Section 23.9: Function Try Blocks In destructor ..................................................................................................... 218
Chapter 24: Implementation-defined behavior ....................................................................................... 219
Section 24.1: Size of integral types .......................................................................................................................... 219
Section 24.2: Char might be unsigned or signed ................................................................................................... 221
Section 24.3: Ranges of numeric types .................................................................................................................. 221
Section 24.4: Value representation of floating point types .................................................................................. 222
Section 24.5: Overflow when converting from integer to signed integer ........................................................... 222
Section 24.6: Underlying type (and hence size) of an enum ................................................................................ 223
Section 24.7: Numeric value of a pointer ................................................................................................................ 223
Section 24.8: Number of bits in a byte .................................................................................................................... 224
Chapter 25: Special Member Functions ......................................................................................................... 224
Section 25.1: Default Constructor ............................................................................................................................ 224
Section 25.2: Destructor ........................................................................................................................................... 226
Section 25.3: Copy and swap ................................................................................................................................... 228
Section 25.4: Implicit Move and Copy ..................................................................................................................... 229
Chapter 26: Random number generation .................................................................................................... 229
Section 26.1: True random value generator ........................................................................................................... 229
Section 26.2: Generating a pseudo-random number ........................................................................................... 230
Section 26.3: Using the generator for multiple distributions ................................................................................ 230
Chapter 27: References ......................................................................................................................................... 231
Section 27.1: Defining a reference ........................................................................................................................... 231
Chapter 28: Sorting ................................................................................................................................................. 231
Section 28.1: Sorting and sequence containers ...................................................................................................... 232
Section 28.2: sorting with std::map (ascending and descending) ....................................................................... 232
Section 28.3: Sorting sequence containers by overloaded less operator ........................................................... 234
Section 28.4: Sorting sequence containers using compare function .................................................................. 235
Section 28.5: Sorting sequence containers using lambda expressions (C++11) .................................................. 235
Section 28.6: Sorting built-in arrays ........................................................................................................................ 236
Section 28.7: Sorting sequence containers with specifed ordering ..................................................................... 237
Chapter 29: Regular expressions ...................................................................................................................... 237
Section 29.1: Basic regex_match and regex_search Examples ........................................................................... 238
Section 29.2: regex_iterator Example ..................................................................................................................... 238
Section 29.3: Anchors ................................................................................................................................................ 239
Section 29.4: regex_replace Example .................................................................................................................... 240
Section 29.5: regex_token_iterator Example ........................................................................................................ 240
Section 29.6: Quantifiers ........................................................................................................................................... 240
Section 29.7: Splitting a string .................................................................................................................................. 241
Chapter 30: Polymorphism .................................................................................................................................. 242
Section 30.1: Define polymorphic classes ............................................................................................................... 242
Section 30.2: Safe downcasting ............................................................................................................................... 243
Section 30.3: Polymorphism & Destructors ............................................................................................................ 244
Chapter 31: Perfect Forwarding ........................................................................................................................ 245
Section 31.1: Factory functions ................................................................................................................................. 245
Chapter 32: Virtual Member Functions .......................................................................................................... 245
Section 32.1: Final virtual functions .......................................................................................................................... 246
Section 32.2: Using override with virtual in C++11 and later .................................................................................. 246
Section 32.3: Virtual vs non-virtual member functions ......................................................................................... 247
Section 32.4: Behaviour of virtual functions in constructors and destructors .................................................... 248
Section 32.5: Pure virtual functions ......................................................................................................................... 249
Chapter 33: Undefined Behavior ....................................................................................................................... 251
Section 33.1: Reading or writing through a null pointer ......................................................................................... 251
Section 33.2: Using an uninitialized local variable ................................................................................................. 251
Section 33.3: Accessing an out-of-bounds index ................................................................................................... 252
Section 33.4: Deleting a derived object via a pointer to a base class that doesn't have a virtual destructor.
............................................................................................................................................................................. 252
Section 33.5: Extending the `std` or `posix` Namespace ........................................................................................ 252
Section 33.6: Invalid pointer arithmetic ................................................................................................................... 253
Section 33.7: No return statement for a function with a non-void return type .................................................. 254
Section 33.8: Accessing a dangling reference ....................................................................................................... 254
Section 33.9: Integer division by zero ...................................................................................................................... 254
Section 33.10: Shifting by an invalid number of positions ..................................................................................... 255
Section 33.11: Incorrect pairing of memory allocation and deallocation ............................................................. 255
Section 33.12: Signed Integer Overflow ................................................................................................................... 255
Section 33.13: Multiple non-identical definitions (the One Definition Rule) .......................................................... 256
Section 33.14: Modifying a const object .................................................................................................................. 257
Section 33.15: Returning from a [[noreturn]] function .......................................................................................... 258
Section 33.16: Infinite template recursion ................................................................................................................ 258
Section 33.17: Overflow during conversion to or from floating point type ......................................................... 258
Section 33.18: Modifying a string literal ................................................................................................................... 258
Section 33.19: Accessing an object as the wrong type .......................................................................................... 259
Section 33.20: Invalid derived-to-base conversion for pointers to members .................................................... 259
Section 33.21: Destroying an object that has already been destroyed ............................................................... 259
Section 33.22: Access to nonexistent member through pointer to member ...................................................... 260
Section 33.23: Invalid base-to-derived static cast ................................................................................................. 260
Section 33.24: Floating point overflow .................................................................................................................... 260
Section 33.25: Calling (Pure) Virtual Members From Constructor Or Destructor .............................................. 260
Section 33.26: Function call through mismatched function pointer type ............................................................ 261
Chapter 34: Value and Reference Semantics ........................................................................................... 261
Section 34.1: Definitions ............................................................................................................................................ 261
Section 34.2: Deep copying and move support ..................................................................................................... 262
Chapter 35: Overload resolution ...................................................................................................................... 264
Section 35.1: Categorization of argument to parameter cost .............................................................................. 264
Section 35.2: Arithmetic promotions and conversions .......................................................................................... 265
Section 35.3: Overloading on Forwarding Reference ........................................................................................... 266
Section 35.4: Exact match ........................................................................................................................................ 266
Section 35.5: Overloading on constness and volatility .......................................................................................... 267
Section 35.6: Name lookup and access checking .................................................................................................. 268
Section 35.7: Overloading within a class hierarchy ............................................................................................... 268
Section 35.8: Steps of Overload Resolution ........................................................................................................... 269
Chapter 36: Move Semantics .............................................................................................................................. 271
Section 36.1: Move semantics ................................................................................................................................... 271
Section 36.2: Using std::move to reduce complexity from O(n²) to O(n) ............................................................ 271
Section 36.3: Move constructor ................................................................................................................................ 274
Section 36.4: Re-use a moved object ...................................................................................................................... 276
Section 36.5: Move assignment ............................................................................................................................... 276
Section 36.6: Using move semantics on containers .............................................................................................. 277
Chapter 37: Pointers to members .................................................................................................................... 278
Section 37.1: Pointers to static member functions ................................................................................................. 278
Section 37.2: Pointers to member functions ........................................................................................................... 278
Section 37.3: Pointers to member variables ........................................................................................................... 279
Section 37.4: Pointers to static member variables ................................................................................................ 280
Chapter 38: Pimpl Idiom ........................................................................................................................................ 280
Section 38.1: Basic Pimpl idiom ................................................................................................................................ 280
Chapter 39: std::function: To wrap any element that is callable .................................................... 281
Section 39.1: Simple usage ....................................................................................................................................... 281
Section 39.2: std::function used with std::bind ........................................................................................................ 282
Section 39.3: Binding std::function to a dierent callable types .......................................................................... 282
Section 39.4: Storing function arguments in std::tuple .......................................................................................... 284
Section 39.5: std::function with lambda and std::bind ............................................................................................ 285
Section 39.6: `function` overhead ............................................................................................................................. 286
Chapter 40: const keyword ................................................................................................................................ 287
Section 40.1: Avoiding duplication of code in const and non-const getter methods. ........................................ 287
Section 40.2: Const member functions ................................................................................................................... 289
Section 40.3: Const local variables .......................................................................................................................... 289
Section 40.4: Const pointers ..................................................................................................................................... 290
Chapter 41: auto ....................................................................................................................................................... 290
Section 41.1: Basic auto sample ............................................................................................................................... 290
Section 41.2: Generic lambda (C++14) ..................................................................................................................... 291
Section 41.3: auto and proxy objects ....................................................................................................................... 291
Section 41.4: auto and Expression Templates ........................................................................................................ 292
Section 41.5: auto, const, and references ................................................................................................................ 292
Section 41.6: Trailing return type ............................................................................................................................. 293
Chapter 42: std::optional ..................................................................................................................................... 293
Section 42.1: Using optionals to represent the absence of a value ..................................................................... 293
Section 42.2: optional as return value .................................................................................................................... 294
Section 42.3: value_or .............................................................................................................................................. 294
Section 42.4: Introduction ......................................................................................................................................... 295
Section 42.5: Using optionals to represent the failure of a function ................................................................... 295
Chapter 43: Copy Elision ....................................................................................................................................... 296
Section 43.1: Purpose of copy elision ...................................................................................................................... 296
Section 43.2: Guaranteed copy elision .................................................................................................................... 297
Section 43.3: Parameter elision ................................................................................................................................ 299
Section 43.4: Return value elision ............................................................................................................................ 299
Section 43.5: Named return value elision ............................................................................................................... 299
Section 43.6: Copy initialization elision .................................................................................................................... 300
Chapter 44: Bit Operators .................................................................................................................................. 300
Section 44.1: | - bitwise OR ........................................................................................................................................ 300
Section 44.2: ^ - bitwise XOR (exclusive OR) .......................................................................................................... 301
Section 44.3: & - bitwise AND ................................................................................................................................... 302
Section 44.4: << - left shift ........................................................................................................................................ 302
Section 44.5: >> - right shift ...................................................................................................................................... 303
Chapter 45: Fold Expressions ............................................................................................................................. 304
Section 45.1: Unary Folds .......................................................................................................................................... 304
Section 45.2: Binary Folds ........................................................................................................................................ 304
Section 45.3: Folding over a comma ....................................................................................................................... 305
Chapter 46: Unions .................................................................................................................................................. 305
Section 46.1: Undefined Behavior ............................................................................................................................ 305
Section 46.2: Basic Union Features ......................................................................................................................... 305
Section 46.3: Typical Use .......................................................................................................................................... 306
Chapter 47: Unnamed types .............................................................................................................................. 306
Section 47.1: Unnamed classes ................................................................................................................................ 306
Section 47.2: As a type alias .................................................................................................................................... 307
Section 47.3: Anonymous members ........................................................................................................................ 307
Section 47.4: Anonymous Union .............................................................................................................................. 307
Chapter 48: mutable keyword .......................................................................................................................... 308
Section 48.1: mutable lambdas ................................................................................................................................ 308
Section 48.2: non-static class member modifier ................................................................................................... 308
Chapter 49: Bit fields .............................................................................................................................................. 309
Section 49.1: Declaration and Usage ....................................................................................................................... 309
Chapter 50: std::array ........................................................................................................................................... 310
Section 50.1: Initializing an std::array ....................................................................................................................... 310
Section 50.2: Element access ................................................................................................................................... 310
Section 50.3: Iterating through the Array ............................................................................................................... 313
Section 50.4: Checking size of the Array ................................................................................................................ 313
Section 50.5: Changing all array elements at once .............................................................................................. 313
Chapter 51: Singleton Design Pattern ............................................................................................................ 313
Section 51.1: Lazy Initialization .................................................................................................................................. 313
Section 51.2: Static deinitialization-safe singleton. ................................................................................................. 314
Section 51.3: Thread-safe Singeton ......................................................................................................................... 315
Section 51.4: Subclasses ............................................................................................................................................ 315
Chapter 52: The ISO C++ Standard ................................................................................................................. 316
Section 52.1: Current Working Drafts ...................................................................................................................... 316
Section 52.2: C++17 .................................................................................................................................................... 316
Section 52.3: C++11 ..................................................................................................................................................... 317
Section 52.4: C++14 .................................................................................................................................................... 319
Section 52.5: C++98 ................................................................................................................................................... 319
Section 52.6: C++03 ................................................................................................................................................... 320
Section 52.7: C++20 ................................................................................................................................................... 320
Chapter 53: User-Defined Literals ................................................................................................................... 320
Section 53.1: Self-made user-defined literal for binary ......................................................................................... 320
Section 53.2: Standard user-defined literals for duration ..................................................................................... 321
Section 53.3: User-defined literals with long double values ................................................................................. 321
Section 53.4: Standard user-defined literals for strings ........................................................................................ 322
Section 53.5: Standard user-defined literals for complex ..................................................................................... 322
Chapter 54: Enumeration ..................................................................................................................................... 323
Section 54.1: Iteration over an enum ....................................................................................................................... 323
Section 54.2: Scoped enums .................................................................................................................................... 324
Section 54.3: Enum forward declaration in C++11 .................................................................................................. 325
Section 54.4: Basic Enumeration Declaration ........................................................................................................ 325
Section 54.5: Enumeration in switch statements ................................................................................................... 326
Chapter 55: Type Erasure .................................................................................................................................... 326
Section 55.1: A move-only `std::function` ................................................................................................................. 326
Section 55.2: Erasing down to a Regular type with manual vtable ..................................................................... 328
Section 55.3: Basic mechanism ................................................................................................................................ 331
Section 55.4: Erasing down to a contiguous buer of T ....................................................................................... 332
Section 55.5: Type erasing type erasure with std::any .......................................................................................... 334
Chapter 56: Memory management ................................................................................................................. 338
Section 56.1: Free Storage (Heap, Dynamic Allocation ...) ..................................................................................... 338
Section 56.2: Placement new ................................................................................................................................... 339
Section 56.3: Stack .................................................................................................................................................... 340
Chapter 57: Bit Manipulation .............................................................................................................................. 341
Section 57.1: Remove rightmost set bit ................................................................................................................... 341
Section 57.2: Set all bits ............................................................................................................................................ 341
Section 57.3: Toggling a bit ...................................................................................................................................... 341
Section 57.4: Checking a bit ..................................................................................................................................... 341
Section 57.5: Counting bits set ................................................................................................................................. 342
Section 57.6: Check if an integer is a power of 2 ................................................................................................... 342
Section 57.7: Setting a bit ......................................................................................................................................... 343
Section 57.8: Clearing a bit ....................................................................................................................................... 343
Section 57.9: Changing the nth bit to x ................................................................................................................... 343
Section 57.10: Bit Manipulation Application: Small to Capital Letter .................................................................... 343
Chapter 58: Arrays ................................................................................................................................................... 344
Section 58.1: Array initialization ............................................................................................................................... 344
Section 58.2: A fixed size raw array matrix (that is, a 2D raw array). ................................................................. 345
Section 58.3: Dynamically sized raw array ............................................................................................................ 346
Section 58.4: Array size: type safe at compile time. .............................................................................................. 346
Section 58.5: Expanding dynamic size array by using std::vector. ...................................................................... 347
Section 58.6: A dynamic size matrix using std::vector for storage. ...................................................................... 348
Chapter 59: Pointers ............................................................................................................................................... 350
Section 59.1: Pointer Operations .............................................................................................................................. 350
Section 59.2: Pointer basics ...................................................................................................................................... 351
Section 59.3: Pointer Arithmetic ............................................................................................................................... 353
Chapter 60: Explicit type conversions ............................................................................................................ 354
Section 60.1: C-style casting ..................................................................................................................................... 354
Section 60.2: Casting away constness .................................................................................................................... 355
Section 60.3: Base to derived conversion ............................................................................................................... 355
Section 60.4: Conversion between pointer and integer ........................................................................................ 356
Section 60.5: Conversion by explicit constructor or explicit conversion function .............................................. 356
Section 60.6: Implicit conversion ............................................................................................................................. 356
Section 60.7: Enum conversions .............................................................................................................................. 357
Section 60.8: Derived to base conversion for pointers to members ................................................................... 358
Section 60.9: void* to T* ............................................................................................................................................ 358
Section 60.10: Type punning conversion ................................................................................................................ 359
Chapter 61: RTTI: Run-Time Type Information ......................................................................................... 359
Section 61.1: dynamic_cast ....................................................................................................................................... 359
Section 61.2: The typeid keyword ............................................................................................................................ 360
Section 61.3: Name of a type ................................................................................................................................... 360
Section 61.4: When to use which cast in c++ ........................................................................................................... 361
Chapter 62: Standard Library Algorithms ................................................................................................... 361
Section 62.1: std::next_permutation ......................................................................................................................... 361
Section 62.2: std::for_each ....................................................................................................................................... 362
Section 62.3: std::accumulate ................................................................................................................................... 362
Section 62.4: std::find ................................................................................................................................................ 363
Section 62.5: std::min_element ................................................................................................................................ 365
Section 62.6: std::find_if ............................................................................................................................................ 366
Section 62.7: Using std::nth_element To Find The Median (Or Other Quantiles) ............................................... 367
Section 62.8: std::count ............................................................................................................................................. 368
Section 62.9: std::count_if ......................................................................................................................................... 369
Chapter 63: Friend keyword ................................................................................................................................ 370
Section 63.1: Friend function ..................................................................................................................................... 370
Section 63.2: Friend method .................................................................................................................................... 371
Section 63.3: Friend class ......................................................................................................................................... 372
Chapter 64: Expression templates .................................................................................................................. 373
Section 64.1: A basic example illustrating expression templates ......................................................................... 373
Chapter 65: Scopes .................................................................................................................................................. 376
Section 65.1: Global variables ................................................................................................................................... 376
Section 65.2: Simple block scope ............................................................................................................................ 377
Chapter 66: Atomic Types .................................................................................................................................... 377
Section 66.1: Multi-threaded Access ........................................................................................................................ 377
Chapter 67: static_assert .................................................................................................................................... 379
Section 67.1: static_assert ........................................................................................................................................ 379
Chapter 68: operator precedence ................................................................................................................... 379
Section 68.1: Logical && and || operators: short-circuit ......................................................................................... 379
Section 68.2: Unary Operators ................................................................................................................................ 380
Section 68.3: Arithmetic operators .......................................................................................................................... 381
Section 68.4: Logical AND and OR operators ........................................................................................................ 381
Chapter 69: constexpr ............................................................................................................................................ 382
Section 69.1: constexpr variables ............................................................................................................................. 382
Section 69.2: Static if statement .............................................................................................................................. 383
Section 69.3: constexpr functions ............................................................................................................................ 384
Chapter 70: Date and time using <chrono> header ............................................................................... 385
Section 70.1: Measuring time using <chrono> ........................................................................................................ 385
Section 70.2: Find number of days between two dates ........................................................................................ 386
Chapter 71: Trailing return type ....................................................................................................................... 387
Section 71.1: Avoid qualifying a nested type name ................................................................................................ 387
Section 71.2: Lambda expressions ........................................................................................................................... 387
Chapter 72: Function Template Overloading ............................................................................................ 387
Section 72.1: What is a valid function template overloading? .............................................................................. 387
Chapter 73: Common compile/linker errors (GCC) ................................................................................. 388
Section 73.1: undefined reference to `***' ................................................................................................................. 388
Section 73.2: error: '***' was not declared in this scope ......................................................................................... 388
Section 73.3: fatal error: ***: No such file or directory ............................................................................................ 390
Chapter 74: Design pattern implementation in C++ .............................................................................. 390
Section 74.1: Adapter Pattern ................................................................................................................................... 390
Section 74.2: Observer pattern ................................................................................................................................ 392
Section 74.3: Factory Pattern ................................................................................................................................... 395
Section 74.4: Builder Pattern with Fluent API .......................................................................................................... 396
Chapter 75: Optimization in C++ ....................................................................................................................... 399
Section 75.1: Introduction to performance ............................................................................................................. 399
Section 75.2: Empty Base Class Optimization ........................................................................................................ 399
Section 75.3: Optimizing by executing less code ................................................................................................... 400
Section 75.4: Using ecient containers .................................................................................................................. 401
Section 75.5: Small Object Optimization ................................................................................................................. 401
Chapter 76: Compiling and Building ................................................................................................................ 403
Section 76.1: Compiling with GCC ............................................................................................................................. 403
Section 76.2: Compiling with Visual Studio (Graphical Interface) - Hello World ................................................ 405
Section 76.3: Online Compilers ................................................................................................................................. 409
Section 76.4: Compiling with Visual C++ (Command Line) ................................................................................... 411
Section 76.5: Compiling with Clang ......................................................................................................................... 414
Section 76.6: The C++ compilation process ............................................................................................................ 414
Section 76.7: Compiling with Code::Blocks (Graphical interface) ......................................................................... 416
Chapter 77: Type Traits ........................................................................................................................................ 418
Section 77.1: Type Properties ................................................................................................................................... 418
Section 77.2: Standard type traits ........................................................................................................................... 419
Section 77.3: Type relations with std::is_same<T, T> ............................................................................................. 420
Section 77.4: Fundamental type traits .................................................................................................................... 421
Chapter 78: std::pair ............................................................................................................................................... 422
Section 78.1: Compare operators ............................................................................................................................ 422
Section 78.2: Creating a Pair and accessing the elements ................................................................................... 423
Chapter 79: Keywords ............................................................................................................................................ 424
Section 79.1: asm ....................................................................................................................................................... 424
Section 79.2: Dierent keywords ............................................................................................................................. 424
Section 79.3: typename ............................................................................................................................................ 428
Section 79.4: explicit .................................................................................................................................................. 429
Section 79.5: sizeof .................................................................................................................................................... 430
Section 79.6: noexcept .............................................................................................................................................. 430
Chapter 80: One Definition Rule (ODR) ......................................................................................................... 431
Section 80.1: ODR violation via overload resolution .............................................................................................. 431
Section 80.2: Multiply defined function ................................................................................................................... 432
Section 80.3: Inline functions .................................................................................................................................... 432
Chapter 81: Unspecified behavior .................................................................................................................... 433
Section 81.1: Value of an out-of-range enum ......................................................................................................... 433
Section 81.2: Evaluation order of function arguments .......................................................................................... 434
Section 81.3: Result of some reinterpret_cast conversions .................................................................................. 435
Section 81.4: Space occupied by a reference ......................................................................................................... 435
Section 81.5: Moved-from state of most standard library classes ...................................................................... 436
Section 81.6: Result of some pointer comparisons ................................................................................................ 436
Section 81.7: Static cast from bogus void* value .................................................................................................... 436
Section 81.8: Order of initialization of globals across TU ...................................................................................... 437
Chapter 82: Floating Point Arithmetic ........................................................................................................... 437
Section 82.1: Floating Point Numbers are Weird .................................................................................................... 437
Chapter 83: Argument Dependent Name Lookup ................................................................................... 438
Section 83.1: What functions are found .................................................................................................................. 438
Chapter 84: std::variant ....................................................................................................................................... 439
Section 84.1: Create pseudo-method pointers ....................................................................................................... 439
Section 84.2: Basic std::variant use ......................................................................................................................... 440
Section 84.3: Constructing a `std::variant` ............................................................................................................... 441
Chapter 85: Attributes ........................................................................................................................................... 441
Section 85.1: [[fallthrough]] ..................................................................................................................................... 441
Section 85.2: [[nodiscard]] ...................................................................................................................................... 442
Section 85.3: [[deprecated]] and [[deprecated("reason")]] ............................................................................... 442
Section 85.4: [[maybe_unused]] ............................................................................................................................ 443
Section 85.5: [[noreturn]] ......................................................................................................................................... 444
Chapter 86: Profiling ............................................................................................................................................... 445
Section 86.1: Profiling with gcc and gprof ............................................................................................................... 445
Section 86.2: Generating callgraph diagrams with gperf2dot ............................................................................. 446
Section 86.3: Profiling CPU Usage with gcc and Google Perf Tools .................................................................... 446
Chapter 87: Return Type Covariance ............................................................................................................ 448
Section 87.1: 2. Covariant result version of the base example, static type checking. ........................................ 448
Section 87.2: 3. Covariant smart pointer result (automated cleanup). ................................................................ 448
Chapter 88: Non-Static Member Functions ................................................................................................. 449
Section 88.1: Non-static Member Functions ........................................................................................................... 450
Section 88.2: Encapsulation ..................................................................................................................................... 451
Section 88.3: Name Hiding & Importing .................................................................................................................. 451
Section 88.4: Virtual Member Functions ................................................................................................................. 453
Section 88.5: Const Correctness .............................................................................................................................. 455
Chapter 89: Recursion in C++ ............................................................................................................................. 456
Section 89.1: Using tail recursion and Fibonnaci-style recursion to solve the Fibonnaci sequence ................. 456
Section 89.2: Recursion with memoization ............................................................................................................. 457
Chapter 90: Callable Objects .............................................................................................................................. 458
Section 90.1: Function Pointers ................................................................................................................................ 458
Section 90.2: Classes with operator() (Functors) .................................................................................................. 458
Chapter 91: std::iomanip ....................................................................................................................................... 459
Section 91.1: std::setprecision .................................................................................................................................... 459
Section 91.2: std::setfill ............................................................................................................................................... 459
Section 91.3: std::setiosflags ..................................................................................................................................... 460
Section 91.4: std::setw ................................................................................................................................................ 461
Chapter 92: Constant class member functions ......................................................................................... 462
Section 92.1: constant member function ................................................................................................................ 462
Chapter 93: Side by Side Comparisons of classic C++ examples solved via C++ vs C++11
vs C++14 vs C++17 ..................................................................................................................................................... 462
Section 93.1: Looping through a container ............................................................................................................. 462
Chapter 94: The This Pointer .............................................................................................................................. 463
Section 94.1: this Pointer ........................................................................................................................................... 463
Section 94.2: Using the this Pointer to Access Member Data ............................................................................... 465
Section 94.3: Using the this Pointer to Dierentiate Between Member Data and Parameters ........................ 466
Section 94.4: this Pointer CV-Qualifiers ................................................................................................................... 467
Section 94.5: this Pointer Ref-Qualifiers .................................................................................................................. 469
Chapter 95: Inline functions ................................................................................................................................ 470
Section 95.1: Non-member inline function definition ............................................................................................. 470
Section 95.2: Member inline functions .................................................................................................................... 470
Section 95.3: What is function inlining? ................................................................................................................... 471
Section 95.4: Non-member inline function declaration ......................................................................................... 471
Chapter 96: Copying vs Assignment ............................................................................................................... 471
Section 96.1: Assignment Operator ......................................................................................................................... 472
Section 96.2: Copy Constructor ............................................................................................................................... 472
Section 96.3: Copy Constructor Vs Assignment Constructor ............................................................................... 473
Chapter 97: Client server examples ................................................................................................................ 474
Section 97.1: Hello TCP Client ................................................................................................................................... 474
Section 97.2: Hello TCP Server ................................................................................................................................. 475
Chapter 98: Header Files ...................................................................................................................................... 478
Section 98.1: Basic Example ..................................................................................................................................... 478
Section 98.2: Templates in Header Files ................................................................................................................. 480
Chapter 99: Const Correctness .......................................................................................................................... 480
Section 99.1: The Basics ............................................................................................................................................ 480
Section 99.2: Const Correct Class Design ............................................................................................................... 481
Section 99.3: Const Correct Function Parameters ................................................................................................. 483
Section 99.4: Const Correctness as Documentation ............................................................................................. 484
Chapter 100: std::atomics ..................................................................................................................................... 488
Section 100.1: atomic types ...................................................................................................................................... 488
Chapter 101: Data Structures in C++ ............................................................................................................... 489
Section 101.1: Linked List implementation in C++ .................................................................................................... 489
Chapter 102: Refactoring Techniques ............................................................................................................ 491
Section 102.1: Goto Cleanup ..................................................................................................................................... 491
Chapter 103: C++ Streams .................................................................................................................................... 492
Section 103.1: String streams .................................................................................................................................... 492
Section 103.2: Printing collections with iostream ................................................................................................... 493
Chapter 104: Parameter packs ......................................................................................................................... 494
Section 104.1: A template with a parameter pack .................................................................................................. 494
Section 104.2: Expansion of a parameter pack ...................................................................................................... 494
Chapter 105: Literals ............................................................................................................................................... 495
Section 105.1: this ....................................................................................................................................................... 495
Section 105.2: Integer literal ..................................................................................................................................... 496
Section 105.3: true ..................................................................................................................................................... 497
Section 105.4: false .................................................................................................................................................... 497
Section 105.5: nullptr ................................................................................................................................................. 497
Chapter 106: Flow Control .................................................................................................................................... 497
Section 106.1: case ..................................................................................................................................................... 497
Section 106.2: switch .................................................................................................................................................. 498
Section 106.3: catch ................................................................................................................................................... 498
Section 106.4: throw .................................................................................................................................................. 499
Section 106.5: default ................................................................................................................................................ 499
Section 106.6: try ........................................................................................................................................................ 500
Section 106.7: if .......................................................................................................................................................... 500
Section 106.8: else ..................................................................................................................................................... 500
Section 106.9: Conditional Structures: if, if..else ...................................................................................................... 500
Section 106.10: goto ................................................................................................................................................... 501
Section 106.11: Jump statements : break, continue, goto, exit. .............................................................................. 502
Section 106.12: return ................................................................................................................................................ 505
Chapter 107: Type Keywords .............................................................................................................................. 505
Section 107.1: class ..................................................................................................................................................... 505
Section 107.2: enum ................................................................................................................................................... 506
Section 107.3: struct ................................................................................................................................................... 508
Section 107.4: union ................................................................................................................................................... 508
Chapter 108: Basic Type Keywords ................................................................................................................. 508
Section 108.1: char ..................................................................................................................................................... 508
Section 108.2: char16_t ............................................................................................................................................. 508
Section 108.3: char32_t ............................................................................................................................................. 509
Section 108.4: int ........................................................................................................................................................ 509
Section 108.5: void ..................................................................................................................................................... 509
Section 108.6: wchar_t .............................................................................................................................................. 509
Section 108.7: float .................................................................................................................................................... 510
Section 108.8: double ................................................................................................................................................ 510
Section 108.9: long ..................................................................................................................................................... 510
Section 108.10: short .................................................................................................................................................. 510
Section 108.11: bool .................................................................................................................................................... 510
Chapter 109: Variable Declaration Keywords ............................................................................................ 511
Section 109.1: decltype .............................................................................................................................................. 511
Section 109.2: const ................................................................................................................................................... 511
Section 109.3: volatile ................................................................................................................................................ 512
Section 109.4: signed ................................................................................................................................................. 512
Section 109.5: unsigned ............................................................................................................................................. 512
Chapter 110: Iteration ............................................................................................................................................. 513
Section 110.1: break .................................................................................................................................................... 513
Section 110.2: continue .............................................................................................................................................. 513
Section 110.3: do ......................................................................................................................................................... 513
Section 110.4: while .................................................................................................................................................... 513
Section 110.5: range-based for loop ........................................................................................................................ 513
Section 110.6: for ........................................................................................................................................................ 514
Chapter 111: type deduction ................................................................................................................................. 514
Section 111.1: Template parameter deduction for constructors ............................................................................ 514
Section 111.2: Auto Type Deduction .......................................................................................................................... 514
Section 111.3: Template Type Deduction ................................................................................................................. 515
Chapter 112: std::any ............................................................................................................................................... 516
Section 112.1: Basic usage ......................................................................................................................................... 516
Chapter 113: C++11 Memory Model .................................................................................................................... 516
Section 113.1: Need for Memory Model .................................................................................................................... 516
Section 113.2: Fence example ................................................................................................................................... 518
Chapter 114: Build Systems ................................................................................................................................. 519
Section 114.1: Generating Build Environment with CMake ..................................................................................... 519
Section 114.2: Compiling with GNU make ................................................................................................................ 519
Section 114.3: Building with SCons ............................................................................................................................ 522
Section 114.4: Autotools (GNU) ................................................................................................................................. 522
Section 114.5: Ninja .................................................................................................................................................... 522
Section 114.6: NMAKE (Microsoft Program Maintenance Utility) .......................................................................... 523
Chapter 115: Concurrency With OpenMP ....................................................................................................... 523
Section 115.1: OpenMP: Parallel Sections ................................................................................................................. 523
Section 115.2: OpenMP: Parallel Sections ................................................................................................................ 524
Section 115.3: OpenMP: Parallel For Loop ............................................................................................................... 524
Section 115.4: OpenMP: Parallel Gathering / Reduction ........................................................................................ 525
Chapter 116: Type Inference ................................................................................................................................ 526
Section 116.1: Data Type: Auto .................................................................................................................................. 526
Section 116.2: Lambda auto ...................................................................................................................................... 526
Section 116.3: Loops and auto .................................................................................................................................. 526
Chapter 117: std::integer_sequence ................................................................................................................ 527
Section 117.1: Turn a std::tuple<T...> into function parameters .............................................................................. 527
Section 117.2: Create a parameter pack consisting of integers ........................................................................... 528
Section 117.3: Turn a sequence of indices into copies of an element .................................................................. 528
Chapter 118: Resource Management .............................................................................................................. 529
Section 118.1: Resource Acquisition Is Initialization ................................................................................................. 529
Section 118.2: Mutexes & Thread Safety .................................................................................................................. 530
Chapter 119: std::set and std::multiset ........................................................................................................... 531
Section 119.1: Changing the default sort of a set .................................................................................................... 531
Section 119.2: Deleting values from a set ................................................................................................................ 533
Section 119.3: Inserting values in a set ..................................................................................................................... 534
Section 119.4: Inserting values in a multiset ............................................................................................................ 536
Section 119.5: Searching values in set and multiset ............................................................................................... 536
Chapter 120: Storage class specifiers ............................................................................................................ 537
Section 120.1: extern .................................................................................................................................................. 537
Section 120.2: register ............................................................................................................................................... 538
Section 120.3: static ................................................................................................................................................... 538
Section 120.4: auto .................................................................................................................................................... 539
Section 120.5: mutable .............................................................................................................................................. 539
Chapter 121: Alignment .......................................................................................................................................... 539
Section 121.1: Controlling alignment ......................................................................................................................... 540
Section 121.2: Querying the alignment of a type ................................................................................................... 540
Chapter 122: Inline variables ............................................................................................................................... 541
Section 122.1: Defining a static data member in the class definition ................................................................... 541
Chapter 123: Linkage specifications ................................................................................................................ 541
Section 123.1: Signal handler for Unix-like operating system ............................................................................... 541
Section 123.2: Making a C library header compatible with C++ ........................................................................... 542
Chapter 124: Curiously Recurring Template Pattern (CRTP) ............................................................. 542
Section 124.1: The Curiously Recurring Template Pattern (CRTP) ....................................................................... 542
Section 124.2: CRTP to avoid code duplication ...................................................................................................... 544
Chapter 125: Using declaration ......................................................................................................................... 545
Section 125.1: Importing names individually from a namespace ......................................................................... 545
Section 125.2: Redeclaring members from a base class to avoid name hiding ................................................. 545
Section 125.3: Inheriting constructors ...................................................................................................................... 546
Chapter 126: Typedef and type aliases ......................................................................................................... 546
Section 126.1: Basic typedef syntax ......................................................................................................................... 546
Section 126.2: More complex uses of typedef ........................................................................................................ 547
Section 126.3: Declaring multiple types with typedef ............................................................................................ 547
Section 126.4: Alias declaration with "using" .......................................................................................................... 547
Chapter 127: Layout of object types .............................................................................................................. 548
Section 127.1: Class types .......................................................................................................................................... 548
Section 127.2: Arithmetic types ................................................................................................................................ 550
Section 127.3: Arrays ................................................................................................................................................. 551
Chapter 128: C incompatibilities ........................................................................................................................ 551
Section 128.1: Reserved Keywords ........................................................................................................................... 551
Section 128.2: Weakly typed pointers ..................................................................................................................... 551
Section 128.3: goto or switch .................................................................................................................................... 551
Chapter 129: std::forward_list ........................................................................................................................... 551
Section 129.1: Example .............................................................................................................................................. 552
Section 129.2: Methods ............................................................................................................................................. 552
Chapter 130: Optimization .................................................................................................................................... 553
Section 130.1: Inline Expansion/Inlining ................................................................................................................... 553
Section 130.2: Empty base optimization ................................................................................................................. 554
Chapter 131: Semaphore ....................................................................................................................................... 554
Section 131.1: Semaphore C++ 11 ............................................................................................................................... 554
Section 131.2: Semaphore class in action ................................................................................................................ 555
Chapter 132: Thread synchronization structures ..................................................................................... 556
Section 132.1: std::condition_variable_any, std::cv_status .................................................................................... 556
Section 132.2: std::shared_lock ................................................................................................................................ 556
Section 132.3: std::call_once, std::once_flag ........................................................................................................... 556
Section 132.4: Object locking for ecient access. ................................................................................................. 557
Chapter 133: C++ Debugging and Debug-prevention Tools & Techniques ................................. 558
Section 133.1: Static analysis ..................................................................................................................................... 558
Section 133.2: Segfault analysis with GDB .............................................................................................................. 559
Section 133.3: Clean code ......................................................................................................................................... 560
Chapter 134: Futures and Promises ................................................................................................................ 562
Section 134.1: Async operation classes .................................................................................................................... 562
Section 134.2: std::future and std::promise .............................................................................................................. 562
Section 134.3: Deferred async example .................................................................................................................. 562
Section 134.4: std::packaged_task and std::future ................................................................................................. 563
Section 134.5: std::future_error and std::future_errc ............................................................................................. 563
Section 134.6: std::future and std::async .................................................................................................................. 564
Chapter 135: More undefined behaviors in C++ ......................................................................................... 566
Section 135.1: Referring to non-static members in initializer lists ......................................................................... 566
Chapter 136: Mutexes .............................................................................................................................................. 566
Section 136.1: Mutex Types ....................................................................................................................................... 566
Section 136.2: std::lock ............................................................................................................................................... 566
Section 136.3: std::unique_lock, std::shared_lock, std::lock_guard ...................................................................... 566
Section 136.4: Strategies for lock classes: std::try_to_lock, std::adopt_lock, std::defer_lock ........................... 567
Section 136.5: std::mutex ........................................................................................................................................... 568
Section 136.6: std::scoped_lock (C++ 17) ................................................................................................................. 569
Chapter 137: Unit Testing in C++ ....................................................................................................................... 569
Section 137.1: Google Test ......................................................................................................................................... 569
Section 137.2: Catch ................................................................................................................................................... 570
Chapter 138: Recursive Mutex ............................................................................................................................ 571
Section 138.1: std::recursive_mutex .......................................................................................................................... 571
Chapter 139: decltype ............................................................................................................................................. 571
Section 139.1: Basic Example .................................................................................................................................... 571
Section 139.2: Another example ............................................................................................................................... 572
Chapter 140: Using std::unordered_map .................................................................................................... 572
Section 140.1: Declaration and Usage ..................................................................................................................... 572
Section 140.2: Some Basic Functions ...................................................................................................................... 572
Chapter 141: Digit separators ............................................................................................................................ 573
Section 141.1: Digit Separator .................................................................................................................................... 573
Chapter 142: C++ function "call by value" vs. "call by reference" .................................................. 573
Section 142.1: Call by value ....................................................................................................................................... 573
Chapter 143: Basic input/output in c++ ........................................................................................................ 574
Section 143.1: user input and standard output ....................................................................................................... 574
Chapter 144: Stream manipulators ................................................................................................................ 575
Section 144.1: Stream manipulators ......................................................................................................................... 575
Section 144.2: Output stream manipulators ........................................................................................................... 580
Section 144.3: Input stream manipulators .............................................................................................................. 581
Chapter 145: C++ Containers .............................................................................................................................. 583
Section 145.1: C++ Containers Flowchart ................................................................................................................. 583
Chapter 146: Arithmitic Metaprogramming ................................................................................................ 583
Section 146.1: Calculating power in O(log n) ........................................................................................................... 583
Credits ............................................................................................................................................................................ 585
You may also like ...................................................................................................................................................... 592
About

Please feel free to share this PDF with anyone for free,
latest version of this book can be downloaded from:
http://GoalKicker.com/CPlusPlusBook

Important notice:
These Complete C++ Tips & Secrets for Professionals series are compiled from
Stack Overflow Documentation via Archive.org, the content is written by the
beautiful people at Stack Overflow, text content is released under Creative
Commons BY-SA, see credits at the end of this book whom contributed to the
various chapters. Images may be copyright of their respective owners unless
otherwise specified

This book creation is not affiliated with C++ group(s) nor Stack Overflow, and all
terms and trademarks belong to their respective company owners

The information presented in this book is not guaranteed to be correct nor


accurate, use at your own risk

Send feedback and corrections to [email protected]

Complete C++ Tips & Secrets for Professionals 1


Chapter 1: Getting started with C++
Version Standard Release Date
C++98 ISO/IEC 14882:1998 1998-09-01
C++03 ISO/IEC 14882:2003 2003-10-16
C++11 ISO/IEC 14882:2011 2011-09-01
C++14 ISO/IEC 14882:2014 2014-12-15
C++17 TBD 2017-01-01
C++20 TBD 2020-01-01

Section 1.1: Hello World


This program prints Hello World! to the standard output stream:

#include <iostream>

int main()
{
std::cout << "Hello World!" << std::endl;
}

See it live on Coliru.

Analysis

Let's examine each part of this code in detail:

#include <iostream> is a preprocessor directive that includes the content of the standard C++ header file
iostream.

iostream is a standard library header file that contains definitions of the standard input and output
streams. These definitions are included in the std namespace, explained below.

The standard input/output (I/O) streams provide ways for programs to get input from and output to an
external system -- usually the terminal.

int main() { ... } defines a new function named main. By convention, the main function is called upon
execution of the program. There must be only one main function in a C++ program, and it must always return
a number of the int type.

Here, the int is what is called the function's return type. The value returned by the main function is an exit
code.

By convention, a program exit code of 0 or EXIT_SUCCESS is interpreted as success by a system that executes
the program. Any other return code is associated with an error.

If no return statement is present, the main function (and thus, the program itself) returns 0 by default. In this
example, we don't need to explicitly write return 0;.

All other functions, except those that return the void type, must explicitly return a value according to their
return type, or else must not return at all.

std::cout << "Hello World!" << std::endl; prints "Hello World!" to the standard output stream:

Complete C++ Tips & Secrets for Professionals 2


std is a namespace, and :: is the scope resolution operator that allows look-ups for objects by name
within a namespace.

There are many namespaces. Here, we use :: to show we want to use cout from the std namespace.
For more information refer to Scope Resolution Operator - Microsoft Documentation.

std::cout is the standard output stream object, defined in iostream, and it prints to the standard
output (stdout).

<< is, in this context, the stream insertion operator, so called because it inserts an object into the
stream object.

The standard library defines the << operator to perform data insertion for certain data types into
output streams. stream << content inserts content into the stream and returns the same, but
updated stream. This allows stream insertions to be chained: std::cout << "Foo" << " Bar"; prints
"FooBar" to the console.

"Hello World!" is a character string literal, or a "text literal." The stream insertion operator for
character string literals is defined in file iostream.

std::endl is a special I/O stream manipulator object, also defined in file iostream. Inserting a
manipulator into a stream changes the state of the stream.

The stream manipulator std::endl does two things: first it inserts the end-of-line character and then it
flushes the stream buffer to force the text to show up on the console. This ensures that the data
inserted into the stream actually appear on your console. (Stream data is usually stored in a buffer and
then "flushed" in batches unless you force a flush immediately.)

An alternate method that avoids the flush is:

std::cout << "Hello World!\n";

where \n is the character escape sequence for the newline character.

The semicolon (;) notifies the compiler that a statement has ended. All C++ statements and class
definitions require an ending/terminating semicolon.

Section 1.2: Comments


A comment is a way to put arbitrary text inside source code without having the C++ compiler interpret it with any
functional meaning. Comments are used to give insight into the design or method of a program.

There are two types of comments in C++:

Single-Line Comments

The double forward-slash sequence // will mark all text until a newline as a comment:

int main()
{
// This is a single-line comment.

Complete C++ Tips & Secrets for Professionals 3


int a; // this also is a single-line comment
int i; // this is another single-line comment
}
C-Style/Block Comments

The sequence /* is used to declare the start of the comment block and the sequence */ is used to declare the end
of comment. All text between the start and end sequences is interpreted as a comment, even if the text is
otherwise valid C++ syntax. These are sometimes called "C-style" comments, as this comment syntax is inherited
from C++'s predecessor language, C:

int main()
{
/*
* This is a block comment.
*/
int a;
}

In any block comment, you can write anything you want. When the compiler encounters the symbol */, it
terminates the block comment:

int main()
{
/* A block comment with the symbol /*
Note that the compiler is not affected by the second /*
however, once the end-block-comment symbol is reached,
the comment ends.
*/
int a;
}

The above example is valid C++ (and C) code. However, having additional /* inside a block comment might result in
a warning on some compilers.

Block comments can also start and end within a single line. For example:

void SomeFunction(/* argument 1 */ int a, /* argument 2 */ int b);


Importance of Comments

As with all programming languages, comments provide several benefits:

Explicit documentation of code to make it easier to read/maintain


Explanation of the purpose and functionality of code
Details on the history or reasoning behind the code
Placement of copyright/licenses, project notes, special thanks, contributor credits, etc. directly in the source
code.

However, comments also have their downsides:

They must be maintained to reflect any changes in the code


Excessive comments tend to make the code less readable

The need for comments can be reduced by writing clear, self-documenting code. A simple example is the use of
explanatory names for variables, functions, and types. Factoring out logically related tasks into discrete functions
goes hand-in-hand with this.

Comment markers used to disable code

Complete C++ Tips & Secrets for Professionals 4


During development, comments can also be used to quickly disable portions of code without deleting it. This is
often useful for testing or debugging purposes, but is not good style for anything other than temporary edits. This
is often referred to as “commenting out”.

Similarly, keeping old versions of a piece of code in a comment for reference purposes is frowned upon, as it
clutters files while offering little value compared to exploring the code's history via a versioning system.

Section 1.3: The standard C++ compilation process


Executable C++ program code is usually produced by a compiler.

A compiler is a program that translates code from a programming language into another form which is (more)
directly executable for a computer. Using a compiler to translate code is called compilation.

C++ inherits the form of its compilation process from its "parent" language, C. Below is a list showing the four major
steps of compilation in C++:

1. The C++ preprocessor copies the contents of any included header files into the source code file, generates
macro code, and replaces symbolic constants defined using #define with their values.
2. The expanded source code file produced by the C++ preprocessor is compiled into assembly language
appropriate for the platform.
3. The assembler code generated by the compiler is assembled into appropriate object code for the platform.
4. The object code file generated by the assembler is linked together with the object code files for any library
functions used to produce an executable file.

Note: some compiled code is linked together, but not to create a final program. Usually, this "linked" code
can also be packaged into a format that can be used by other programs. This "bundle of packaged, usable
code" is what C++ programmers refer to as a library.

Many C++ compilers may also merge or un-merge certain parts of the compilation process for ease or for additional
analysis. Many C++ programmers will use different tools, but all of the tools will generally follow this generalized
process when they are involved in the production of a program.

The link below extends this discussion and provides a nice graphic to help. [1]:
http://faculty.cs.niu.edu/~mcmahon/CS241/Notes/compile.html

Section 1.4: Function


A function is a unit of code that represents a sequence of statements.

Functions can accept arguments or values and return a single value (or not). To use a function, a function call is
used on argument values and the use of the function call itself is replaced with its return value.

Every function has a type signature -- the types of its arguments and the type of its return type.

Functions are inspired by the concepts of the procedure and the mathematical function.

Note: C++ functions are essentially procedures and do not follow the exact definition or rules of
mathematical functions.

Functions are often meant to perform a specific task. and can be called from other parts of a program. A function
must be declared and defined before it is called elsewhere in a program.

Note: popular function definitions may be hidden in other included files (often for convenience and reuse
across many files). This is a common use of header files.

Complete C++ Tips & Secrets for Professionals 5


Function Declaration

A function declaration is declares the existence of a function with its name and type signature to the compiler.
The syntax is as the following:

int add2(int i); // The function is of the type (int) -> (int)

In the example above, the int add2(int i) function declares the following to the compiler:

The return type is int.


The name of the function is add2.
The number of arguments to the function is 1:
The first argument is of the type int.
The first argument will be referred to in the function's contents by the name i.

The argument name is optional; the declaration for the function could also be the following:

int add2(int); // Omitting the function arguments' name is also permitted.

Per the one-definition rule, a function with a certain type signature can only be declared or defined once in an
entire C++ code base visible to the C++ compiler. In other words, functions with a specific type signature cannot be
re-defined -- they must only be defined once. Thus, the following is not valid C++:

int add2(int i); // The compiler will note that add2 is a function (int) -> int
int add2(int j); // As add2 already has a definition of (int) -> int, the compiler
// will regard this as an error.

If a function returns nothing, its return type is written as void. If it takes no parameters, the parameter list should
be empty.

void do_something(); // The function takes no parameters, and does not return anything.
// Note that it can still affect variables it has access to.
Function Call

A function can be called after it has been declared. For example, the following program calls add2 with the value of
2 within the function of main:

#include <iostream>

int add2(int i); // Declaration of add2

// Note: add2 is still missing a DEFINITION.


// Even though it doesn't appear directly in code,
// add2's definition may be LINKED in from another object file.

int main()
{
std::cout << add2(2) << "\n"; // add2(2) will be evaluated at this point,
// and the result is printed.
return 0;
}

Here, add2(2) is the syntax for a function call.

Function Definition

A function definition* is similar to a declaration, except it also contains the code that is executed when the function
is called within its body.

Complete C++ Tips & Secrets for Professionals 6


An example of a function definition for add2 might be:

int add2(int i) // Data that is passed into (int i) will be referred to by the name i
{ // while in the function's curly brackets or "scope."

int j = i + 2; // Definition of a variable j as the value of i+2.


return j; // Returning or, in essence, substitution of j for a function call to
// add2.
}
Function Overloading

You can create multiple functions with the same name but different parameters.

int add2(int i) // Code contained in this definition will be evaluated


{ // when add2() is called with one parameter.
int j = i + 2;
return j;
}

int add2(int i, int j) // However, when add2() is called with two parameters, the
{ // code from the initial declaration will be overloaded,
int k = i + j + 2 ; // and the code in this declaration will be evaluated
return k; // instead.
}

Both functions are called by the same name add2, but the actual function that is called depends directly on the
amount and type of the parameters in the call. In most cases, the C++ compiler can compute which function to call.
In some cases, the type must be explicitly stated.

Default Parameters

Default values for function parameters can only be specified in function declarations.

int multiply(int a, int b = 7); // b has default value of 7.


int multiply(int a, int b)
{
return a * b; // If multiply() is called with one parameter, the
} // value will be multiplied by the default, 7.

In this example, multiply() can be called with one or two parameters. If only one parameter is given, b will have
default value of 7. Default arguments must be placed in the latter arguments of the function. For example:

int multiply(int a = 10, int b = 20); // This is legal


int multiply(int a = 10, int b); // This is illegal since int a is in the former
Special Function Calls - Operators

There exist special function calls in C++ which have different syntax than name_of_function(value1, value2,
value3). The most common example is that of operators.

Certain special character sequences that will be reduced to function calls by the compiler, such as !, +, -, *, %, and
<< and many more. These special characters are normally associated with non-programming usage or are used for
aesthetics (e.g. the + character is commonly recognized as the addition symbol both within C++ programming as
well as in elementary math).

C++ handles these character sequences with a special syntax; but, in essence, each occurrence of an operator is
reduced to a function call. For example, the following C++ expression:

3+3

Complete C++ Tips & Secrets for Professionals 7


is equivalent to the following function call:

operator+(3, 3)

All operator function names start with operator.

While in C++'s immediate predecessor, C, operator function names cannot be assigned different meanings by
providing additional definitions with different type signatures, in C++, this is valid. "Hiding" additional function
definitions under one unique function name is referred to as operator overloading in C++, and is a relatively
common, but not universal, convention in C++.

Section 1.5: Visibility of function prototypes and declarations


In C++, code must be declared or defined before usage. For example, the following produces a compile time error:

int main()
{
foo(2); // error: foo is called, but has not yet been declared
}

void foo(int x) // this later definition is not known in main


{
}

There are two ways to resolve this: putting either the definition or declaration of foo() before its usage in main().
Here is one example:

void foo(int x) {} //Declare the foo function and body first

int main()
{
foo(2); // OK: foo is completely defined beforehand, so it can be called here.
}

However it is also possible to "forward-declare" the function by putting only a "prototype" declaration before its
usage and then defining the function body later:

void foo(int); // Prototype declaration of foo, seen by main


// Must specify return type, name, and argument list types
int main()
{
foo(2); // OK: foo is known, called even though its body is not yet defined
}

void foo(int x) //Must match the prototype


{
// Define body of foo here
}

The prototype must specify the return type (void), the name of the function (foo), and the argument list variable
types (int), but the names of the arguments are NOT required.

One common way to integrate this into the organization of source files is to make a header file containing all of the
prototype declarations:

// foo.h
void foo(int); // prototype declaration

and then provide the full definition elsewhere:

Complete C++ Tips & Secrets for Professionals 8


// foo.cpp --> foo.o
#include "foo.h" // foo's prototype declaration is "hidden" in here
void foo(int x) { } // foo's body definition

and then, once compiled, link the corresponding object file foo.o into the compiled object file where it is used in
the linking phase, main.o:

// main.cpp --> main.o


#include "foo.h" // foo's prototype declaration is "hidden" in here
int main() { foo(2); } // foo is valid to call because its prototype declaration was beforehand.
// the prototype and body definitions of foo are linked through the object files

An “unresolved external symbol” error occurs when the function prototype and call exist, but the function body is
not defined. These can be trickier to resolve as the compiler won't report the error until the final linking stage, and
it doesn't know which line to jump to in the code to show the error.

Section 1.6: Preprocessor


The preprocessor is an important part of the compiler.

It edits the source code, cutting some bits out, changing others, and adding other things.

In source files, we can include preprocessor directives. These directives tells the preprocessor to perform specific
actions. A directive starts with a # on a new line. Example:

#define ZERO 0

The first preprocessor directive you will meet is probably the

#include <something>

directive. What it does is takes all of something and inserts it in your file where the directive was. The hello world
program starts with the line

#include <iostream>

This line adds the functions and objects that let you use the standard input and output.

The C language, which also uses the preprocessor, does not have as many header files as the C++ language, but in
C++ you can use all the C header files.

The next important directive is probably the

#define something something_else

directive. This tells the preprocessor that as it goes along the file, it should replace every occurrence of something
with something_else. It can also make things similar to functions, but that probably counts as advanced C++.

The something_else is not needed, but if you define something as nothing, then outside preprocessor directives, all
occurrences of something will vanish.

This actually is useful, because of the #if,#else and #ifdef directives. The format for these would be the following:

#if something==true
//code
#else
//more code
#endif

Complete C++ Tips & Secrets for Professionals 9


#ifdef thing_that_you_want_to_know_if_is_defined
//code
#endif

These directives insert the code that is in the true bit, and deletes the false bits. this can be used to have bits of
code that are only included on certain operating systems, without having to rewrite the whole code.

Chapter 2: Templates
template < template-parameter-list > declaration
export template < template-parameter-list > declaration /* until C++11 */
template <> declaration
template declaration
extern template declaration /* since C++11 */
template < template-parameter-list > class ...(opt) identifier(opt)
template < template-parameter-list > class identifier(opt) = id-expression
template < template-parameter-list > typename ...(opt) identifier(opt) /* since C++17 */
template < template-parameter-list > typename identifier(opt) = id-expression /* since C++17 */
postfix-expression . template id-expression
postfix-expression -> template id-expression
nested-name-specifier template simple-template-id ::

Classes, functions, and (since C++14) variables can be templated. A template is a piece of code with some free
parameters that will become a concrete class, function, or variable when all parameters are specified. Parameters
can be types, values, or themselves templates. A well-known template is std::vector, which becomes a concrete
container type when the element type is specified, e.g., std::vector<int>.

Section 2.1: Basic Class Template


The basic idea of a class template is that the template parameter gets substituted by a type at compile time. The
result is that the same class can be reused for multiple types. The user specifies which type will be used when a
variable of the class is declared. Three examples of this are shown in main():

#include <iostream>
using std::cout;

template <typename T> // A simple class to hold one number of any type
class Number {
public:
void setNum(T n); // Sets the class field to the given number
T plus1() const; // returns class field's "follower"
private:
T num; // Class field
};

template <typename T> // Set the class field to the given number
void Number<T>::setNum(T n) {
num = n;
}

template <typename T> // returns class field's "follower"


T Number<T>::plus1() const {
return num + 1;
}

int main() {
Number<int> anInt; // Test with an integer (int replaces T in the class)

Complete C++ Tips & Secrets for Professionals 10


anInt.setNum(1);
cout << "My integer + 1 is " << anInt.plus1() << "\n"; // Prints 2

Number<double> aDouble; // Test with a double


aDouble.setNum(3.1415926535897);
cout << "My double + 1 is " << aDouble.plus1() << "\n"; // Prints 4.14159

Number<float> aFloat; // Test with a float


aFloat.setNum(1.4);
cout << "My float + 1 is " << aFloat.plus1() << "\n"; // Prints 2.4

return 0; // Successful completion


}

Section 2.2: Function Templates


Templating can also be applied to functions (as well as the more traditional structures) with the same effect.

// 'T' stands for the unknown type


// Both of our arguments will be of the same type.
template<typename T>
void printSum(T add1, T add2)
{
std::cout << (add1 + add2) << std::endl;
}

This can then be used in the same way as structure templates.

printSum<int>(4, 5);
printSum<float>(4.5f, 8.9f);

In both these case the template argument is used to replace the types of the parameters; the result works just like
a normal C++ function (if the parameters don't match the template type the compiler applies the standard
conversions).

One additional property of template functions (unlike template classes) is that the compiler can infer the template
parameters based on the parameters passed to the function.

printSum(4, 5); // Both parameters are int.


// This allows the compiler deduce that the type
// T is also int.

printSum(5.0, 4); // In this case the parameters are two different types.
// The compiler is unable to deduce the type of T
// because there are contradictions. As a result
// this is a compile time error.

This feature allows us to simplify code when we combine template structures and functions. There is a common
pattern in the standard library that allows us to make template structure X using a helper function make_X().

// The make_X pattern looks like this.


// 1) A template structure with 1 or more template types.
template<typename T1, typename T2>
struct MyPair
{
T1 first;
T2 second;
};
// 2) A make function that has a parameter type for
// each template parameter in the template structure.
template<typename T1, typename T2>

Complete C++ Tips & Secrets for Professionals 11


MyPair<T1, T2> make_MyPair(T1 t1, T2 t2)
{
return MyPair<T1, T2>{t1, t2};
}

How does this help?

auto val1 = MyPair<int, float>{5, 8.7}; // Create object explicitly defining the types
auto val2 = make_MyPair(5, 8.7); // Create object using the types of the paramters.
// In this code both val1 and val2 are the same
// type.

Note: This is not designed to shorten the code. This is designed to make the code more robust. It allows the types
to be changed by changing the code in a single place rather than in multiple locations.

Section 2.3: Variadic template data structures


Version≥C++14

It is often useful to define classes or structures that have a variable number and type of data members which are
defined at compile time. The canonical example is std::tuple, but sometimes is it is necessary to define your own
custom structures. Here is an example that defines the structure using compounding (rather than inheritance as
with std::tuple. Start with the general (empty) definition, which also serves as the base-case for recrusion
termination in the later specialisation:

template<typename ... T>


struct DataStructure {};

This already allows us to define an empty structure, DataStructure<> data, albeit that isn't very useful yet.

Next comes the recursive case specialisation:

template<typename T, typename ... Rest>


struct DataStructure<T, Rest ...>
{
DataStructure(const T& first, const Rest& ... rest)
: first(first)
, rest(rest...)
{}

T first;
DataStructure<Rest ... > rest;
};

This is now sufficient for us to create arbitrary data structures, like DataStructure<int, float, std::string>
data(1, 2.1, "hello").

So what's going on? First, note that this is a specialisation whose requirement is that at least one variadic template
parameter (namely T above) exists, whilst not caring about the specific makeup of the pack Rest. Knowing that T
exists allows the definition of its data member, first. The rest of the data is recursively packaged as
DataStructure<Rest ... > rest. The constructor initiates both of those members, including a recursive
constructor call to the rest member.

To understand this better, we can work through an example: suppose you have a declaration DataStructure<int,
float> data. The declaration first matches against the specialisation, yielding a structure with int first and
DataStructure<float> rest data members. The rest definition again matches this specialisation, creating its own
float first and DataStructure<> rest members. Finally this last rest matches against the base-case defintion,
producing an empty structure.

Complete C++ Tips & Secrets for Professionals 12


You can visualise this as follows:

DataStructure<int, float>
-> int first
-> DataStructure<float> rest
-> float first
-> DataStructure<> rest
-> (empty)

Now we have the data structure, but its not terribly useful yet as we cannot easily access the individual data
elements (for example to access the last member of DataStructure<int, float, std::string> data we would
have to use data.rest.rest.first, which is not exactly user-friendly). So we add a get method to it (only needed
in the specialisation as the base-case structure has no data to get):

template<typename T, typename ... Rest>


struct DataStructure<T, Rest ...>
{
...
template<size_t idx>
auto get()
{
return GetHelper<idx, DataStructure<T,Rest...>>::get(*this);
}
...
};

As you can see this get member function is itself templated - this time on the index of the member that is needed
(so usage can be things like data.get<1>(), similar to std::tuple). The actual work is done by a static function in a
helper class, GetHelper. The reason we can't define the required functionality directly in DataStructure's get is
because (as we will shortly see) we would need to specialise on idx - but it isn't possible to specialise a template
member function without specialising the containing class template. Note also the use of a C++14-style auto here
makes our lives significantly simpler as otherwise we would need quite a complicated expression for the return
type.

So on to the helper class. This time we will need an empty forward declaration and two specialisations. First the
declaration:

template<size_t idx, typename T>


struct GetHelper;

Now the base-case (when idx==0). In this case we just return the first member:

template<typename T, typename ... Rest>


struct GetHelper<0, DataStructure<T, Rest ... >>
{
static T get(DataStructure<T, Rest...>& data)
{
return data.first;
}
};

In the recursive case, we decrement idx and invoke the GetHelper for the rest member:

template<size_t idx, typename T, typename ... Rest>


struct GetHelper<idx, DataStructure<T, Rest ... >>
{
static auto get(DataStructure<T, Rest...>& data)
{
return GetHelper<idx-1, DataStructure<Rest ...>>::get(data.rest);

Complete C++ Tips & Secrets for Professionals 13


}
};

To work through an example, suppose we have DataStructure<int, float> data and we need data.get<1>().
This invokes GetHelper<1, DataStructure<int, float>>::get(data) (the 2nd specialisation), which in turn
invokes GetHelper<0, DataStructure<float>>::get(data.rest), which finally returns (by the 1st specialisation as
now idx is 0) data.rest.first.

So that's it! Here is the whole functioning code, with some example use in the main function:

#include <iostream>

template<size_t idx, typename T>


struct GetHelper;

template<typename ... T>


struct DataStructure
{
};

template<typename T, typename ... Rest>


struct DataStructure<T, Rest ...>
{
DataStructure(const T& first, const Rest& ... rest)
: first(first)
, rest(rest...)
{}

T first;
DataStructure<Rest ... > rest;

template<size_t idx>
auto get()
{
return GetHelper<idx, DataStructure<T,Rest...>>::get(*this);
}
};

template<typename T, typename ... Rest>


struct GetHelper<0, DataStructure<T, Rest ... >>
{
static T get(DataStructure<T, Rest...>& data)
{
return data.first;
}
};

template<size_t idx, typename T, typename ... Rest>


struct GetHelper<idx, DataStructure<T, Rest ... >>
{
static auto get(DataStructure<T, Rest...>& data)
{
return GetHelper<idx-1, DataStructure<Rest ...>>::get(data.rest);
}
};

int main()
{
DataStructure<int, float, std::string> data(1, 2.1, "Hello");

std::cout << data.get<0>() << std::endl;

Complete C++ Tips & Secrets for Professionals 14


std::cout << data.get<1>() << std::endl;
std::cout << data.get<2>() << std::endl;

return 0;
}

Section 2.4: Argument forwarding


Template may accept both lvalue and rvalue references using forwarding reference:

template <typename T>


void f(T &&t);

In this case, the real type of t will be deduced depending on the context:

struct X { };

X x;
f(x); // calls f<X&>(x)
f(X()); // calls f<X>(x)

In the first case, the type T is deduced as reference to X (X&), and the type of t is lvalue reference to X, while in the
second case the type of T is deduced as X and the type of t as rvalue reference to X (X&&).

Note: It is worth noticing that in the first case, decltype(t) is the same as T, but not in the second.

In order to perfectly forward t to another function ,whether it is an lvalue or rvalue reference, one must use
std::forward:

template <typename T>


void f(T &&t) {
g(std::forward<T>(t));
}

Forwarding references may be used with variadic templates:

template <typename... Args>


void f(Args&&... args) {
g(std::forward<Args>(args)...);
}

Note: Forwarding references can only be used for template parameters, for instance, in the following code, v is a
rvalue reference, not a forwarding reference:

#include <vector>

template <typename T>


void f(std::vector<T> &&v);

Section 2.5: Partial template specialization


In contrast of a full template specialization partial template specialization allows to introduce template with some of
the arguments of existing template fixed. Partial template specialization is only available for template class/structs:

// Common case:
template<typename T, typename U>
struct S {
T t_val;
U u_val;
};

Complete C++ Tips & Secrets for Professionals 15


// Special case when the first template argument is fixed to int
template<typename V>
struct S<int, V> {
double another_value;
int foo(double arg) {// Do something}
};

As shown above, partial template specializations may introduce completely different sets of data and function
members.

When a partially specialized template is instantiated, the most suitable specialization is selected. For example, let's
define a template and two partial specializations:

template<typename T, typename U, typename V>


struct S {
static void foo() {
std::cout << "General case\n";
}
};

template<typename U, typename V>


struct S<int, U, V> {
static void foo() {
std::cout << "T = int\n";
}
};

template<typename V>
struct S<int, double, V> {
static void foo() {
std::cout << "T = int, U = double\n";
}
};

Now the following calls:

S<std::string, int, double>::foo();


S<int, float, std::string>::foo();
S<int, double, std::string>::foo();

will print

General case
T = int
T = int, U = double

Function templates may only be fully specialized:

template<typename T, typename U>


void foo(T t, U u) {
std::cout << "General case: " << t << " " << u << std::endl;
}

// OK.
template<>
void foo<int, int>(int a1, int a2) {
std::cout << "Two ints: " << a1 << " " << a2 << std::endl;
}

void invoke_foo() {
foo(1, 2.1); // Prints "General case: 1 2.1"

Complete C++ Tips & Secrets for Professionals 16


foo(1,2); // Prints "Two ints: 1 2"
}

// Compilation error: partial function specialization is not allowed.


template<typename U>
void foo<std::string, U>(std::string t, U u) {
std::cout << "General case: " << t << " " << u << std::endl;
}

Section 2.6: Template Specialization


You can define implementation for specific instantiations of a template class/method.

For example if you have:

template <typename T>


T sqrt(T t) { /* Some generic implementation */ }

You can then write:

template<>
int sqrt<int>(int i) { /* Highly optimized integer implementation */ }

Then a user that writes sqrt(4.0) will get the generic implementation whereas sqrt(4) will get the specialized
implementation.

Section 2.7: Alias template


Version≥C++11

Basic example:

template<typename T> using pointer = T*;

This definition makes pointer<T> an alias of T*. For example:

pointer<int> p = new int; // equivalent to: int* p = new int;

Alias templates cannot be specialized. However, that functionality can be obtained indirectly by having them refer
to a nested type in a struct:

template<typename T>
struct nonconst_pointer_helper { typedef T* type; };

template<typename T>
struct nonconst_pointer_helper<T const> { typedef T* type; };

template<typename T> using nonconst_pointer = nonconst_pointer_helper<T>::type;

Section 2.8: Explicit instantiation


An explicit instantiation definition creates and declares a concrete class, function, or variable from a template,
without using it just yet. An explicit instantiation can be referenced from other translation units. This can be used to
avoid defining a template in a header file, if it will only be instantiated with a finite set of arguments. For example:

// print_string.h
template <class T>
void print_string(const T* str);

// print_string.cpp
#include "print_string.h"

Complete C++ Tips & Secrets for Professionals 17


template void print_string(const char*);
template void print_string(const wchar_t*);

Because print_string<char> and print_string<wchar_t> are explicitly instantiated in print_string.cpp, the


linker will be able to find them even though the print_string template is not defined in the header. If these explicit
instantiation declarations were not present, a linker error would likely occur. See Why can templates only be
implemented in the header file?

Version≥C++11

If an explicit instantiation definition is preceded by the extern keyword, it becomes an explicit instantiation
declaration instead. The presence of an explicit instantiation declaration for a given specialization prevents the
implicit instantiation of the given specialization within the current translation unit. Instead, a reference to that
specialization that would otherwise cause an implicit instantiation can refer to an explicit instantiation definition in
the same or another TU.

foo.h

#ifndef FOO_H
#define FOO_H
template <class T> void foo(T x) {
// complicated implementation
}
#endif

foo.cpp

#include "foo.h"
// explicit instantiation definitions for common cases
template void foo(int);
template void foo(double);

main.cpp

#include "foo.h"
// we already know foo.cpp has explicit instantiation definitions for these
extern template void foo(double);
int main() {
foo(42); // instantiates foo<int> here;
// wasteful since foo.cpp provides an explicit instantiation already!
foo(3.14); // does not instantiate foo<double> here;
// uses instantiation of foo<double> in foo.cpp instead
}

Section 2.9: Non-type template parameter


Apart from types as a template parameter we are allowed to declare values of constant expressions meeting one of
the following criteria:

integral or enumeration type,


pointer to object or pointer to function,
lvalue reference to object or lvalue reference to function,
pointer to member,
std::nullptr_t.

Like all template parameters, non-type template parameters can be explicitly specified, defaulted, or derived
implicitly via Template Argument Deduction.

Example of non-type template parameter usage:

Complete C++ Tips & Secrets for Professionals 18


#include <iostream>

template<typename T, std::size_t size>


std::size_t size_of(T (&anArray)[size]) // Pass array by reference. Requires.
{ // an exact size. We allow all sizes
return size; // by using a template "size".
}

int main()
{
char anArrayOfChar[15];
std::cout << "anArrayOfChar: " << size_of(anArrayOfChar) << "\n";

int anArrayOfData[] = {1,2,3,4,5,6,7,8,9};


std::cout << "anArrayOfData: " << size_of(anArrayOfData) << "\n";
}

Example of explicitly specifying both type and non-type template parameters:

#include <array>
int main ()
{
std::array<int, 5> foo; // int is a type parameter, 5 is non-type
}

Non-type template parameters are one of the ways to achieve template recurrence and enables to do
Metaprogramming.

Section 2.10: Declaring non-type template arguments with auto


Prior to C++17, when writing a template non-type parameter, you had to specify its type first. So a common pattern
became writing something like:

template <class T, T N>


struct integral_constant {
using type = T;
static constexpr T value = N;
};

using five = integral_constant<int, 5>;

But for complicated expressions, using something like this involves having to write decltype(expr), expr when
instantiating templates. The solution is to simplify this idiom and simply allow auto:

Version≥C++17
template <auto N>
struct integral_constant {
using type = decltype(N);
static constexpr type value = N;
};

using five = integral_constant<5>;


Empty custom deleter for unique_ptr

A nice motivating example can come from trying to combine the empty base optimization with a custom deleter for
unique_ptr. Different C API deleters have different return types, but we don't care - we just want something to
work for any function:

template <auto DeleteFn>


struct FunctionDeleter {
template <class T>

Complete C++ Tips & Secrets for Professionals 19


void operator()(T* ptr) const {
DeleteFn(ptr);
}
};

template <T, auto DeleteFn>


using unique_ptr_deleter = std::unique_ptr<T, FunctionDeleter<DeleteFn>>;

And now you can simply use any function pointer that can take an argument of type T as a template non-type
parameter, regardless of return type, and get a no-size overhead unique_ptr out of it:

unique_ptr_deleter<std::FILE, std::fclose> p;

Section 2.11: Template template parameters


Sometimes we would like to pass into the template a template type without fixing its values. This is what template
template parameters are created for. Very simple template template parameter examples:

template <class T>


struct Tag1 { };

template <class T>


struct Tag2 { };

template <template <class> class Tag>


struct IntTag {
typedef Tag<int> type;
};

int main() {
IntTag<Tag1>::type t;
}
Version≥C++11
#include <vector>
#include <iostream>

template <class T, template <class...> class C, class U>


C<T> cast_all(const C<U> &c) {
C<T> result(c.begin(), c.end());
return result;
}

int main() {
std::vector<float> vf = {1.2, 2.6, 3.7};
auto vi = cast_all<int>(vf);
for(auto &&i: vi) {
std::cout << i << std::endl;
}
}

Section 2.12: Default template parameter value


Just like in case of the function arguments, template parameters can have their default values. All template
parameters with a default value have to be declared at the end of the template parameter list. The basic idea is that
the template parameters with default value can be omitted while template instantiation.

Simple example of default template parameter value usage:

template <class T, size_t N = 10>


struct my_array {
T arr[N];

Complete C++ Tips & Secrets for Professionals 20


};

int main() {
/* Default parameter is ignored, N = 5 */
my_array<int, 5> a;

/* Print the length of a.arr: 5 */


std::cout << sizeof(a.arr) / sizeof(int) << std::endl;

/* Last parameter is omitted, N = 10 */


my_array<int> b;

/* Print the length of a.arr: 10 */


std::cout << sizeof(b.arr) / sizeof(int) << std::endl;
}

Chapter 3: Metaprogramming
In C++ Metaprogramming refers to the use of macros or templates to generate code at compile-time.

In general, macros are frowned upon in this role and templates are preferred, although they are not as generic.

Template metaprogramming often makes use of compile-time computations, whether via templates or constexpr
functions, to achieve its goals of generating code, however compile-time computations are not metaprogramming
per se.

Section 3.1: Calculating Factorials


Factorials can be computed at compile-time using template metaprogramming techniques.

#include <iostream>

template<unsigned int n>


struct factorial
{
enum
{
value = n * factorial<n - 1>::value
};
};

template<>
struct factorial<0>
{
enum { value = 1 };
};

int main()
{
std::cout << factorial<7>::value << std::endl; // prints "5040"
}

factorial is a struct, but in template metaprogramming it is treated as a template metafunction. By convention,


template metafunctions are evaluated by checking a particular member, either ::type for metafunctions that result
in types, or ::value for metafunctions that generate values.

In the above code, we evaluate the factorial metafunction by instantiating the template with the parameters we
want to pass, and using ::value to get the result of the evaluation.

Complete C++ Tips & Secrets for Professionals 21


The metafunction itself relies on recursively instantiating the same metafunction with smaller values. The
factorial<0> specialization represents the terminating condition. Template metaprogramming has most of the
restrictions of a functional programming language, so recursion is the primary "looping" construct.

Since template metafunctions execute at compile time, their results can be used in contexts that require compile-
time values. For example:

int my_array[factorial<5>::value];

Automatic arrays must have a compile-time defined size. And the result of a metafunction is a compile-time
constant, so it can be used here.

Limitation: Most of the compilers won't allow recursion depth beyond a limit. For example, g++ compiler by default
limits recursion depeth to 256 levels. In case of g++, programmer can set recursion depth using -ftemplate-depth-
X option.

Version≥C++11

Since C++11, the std::integral_constant template can be used for this kind of template computation:

#include <iostream>
#include <type_traits>

template<long long n>


struct factorial :
std::integral_constant<long long, n * factorial<n - 1>::value> {};

template<>
struct factorial<0> :
std::integral_constant<long long, 1> {};

int main()
{
std::cout << factorial<7>::value << std::endl; // prints "5040"
}

Additionally, constexpr functions become a cleaner alternative.

#include <iostream>

constexpr long long factorial(long long n)


{
return (n == 0) ? 1 : n * factorial(n - 1);
}

int main()
{
char test[factorial(3)];
std::cout << factorial(7) << '\n';
}

The body of factorial() is written as a single statement because in C++11 constexpr functions can only use a
quite limited subset of the language.

Version≥C++14

Since C++14, many restrictions for constexpr functions have been dropped and they can now be written much
more conveniently:

constexpr long long factorial(long long n)

Complete C++ Tips & Secrets for Professionals 22


{
if (n == 0)
return 1;
else
return n * factorial(n - 1);
}

Or even:

constexpr long long factorial(int n)


{
long long result = 1;
for (int i = 1; i <= n; ++i) {
result *= i;
}
return result;
}
Version≥C++17

Since c++17 one can use fold expression to calculate factorial:

#include <iostream>
#include <utility>

template <class T, T N, class I = std::make_integer_sequence<T, N>>


struct factorial;

template <class T, T N, T... Is>


struct factorial<T,N,std::index_sequence<T, Is...>> {
static constexpr T value = (static_cast<T>(1) * ... * (Is + 1));
};

int main() {
std::cout << factorial<int, 5>::value << std::endl;
}

Section 3.2: Iterating over a parameter pack


Often, we need to perform an operation over every element in a variadic template parameter pack. There are many
ways to do this, and the solutions get easier to read and write with C++17. Suppose we simply want to print every
element in a pack. The simplest solution is to recurse:

Version≥C++11
void print_all(std::ostream& os) {
// base case
}

template <class T, class... Ts>


void print_all(std::ostream& os, T const& first, Ts const&... rest) {
os << first;

print_all(os, rest...);
}

We could instead use the expander trick, to perform all the streaming in a single function. This has the advantage of
not needing a second overload, but has the disadvantage of less than stellar readability:

Version≥C++11
template <class... Ts>
void print_all(std::ostream& os, Ts const&... args) {
using expander = int[];
(void)expander{0,

Complete C++ Tips & Secrets for Professionals 23


(void(os << args), 0)...
};
}

For an explanation of how this works, see T.C's excellent answer.

Version≥C++17

With C++17, we get two powerful new tools in our arsenal for solving this problem. The first is a fold-expression:

template <class... Ts>


void print_all(std::ostream& os, Ts const&... args) {
((os << args), ...);
}

And the second is if constexpr, which allows us to write our original recursive solution in a single function:

template <class T, class... Ts>


void print_all(std::ostream& os, T const& first, Ts const&... rest) {
os << first;

if constexpr (sizeof...(rest) > 0) {


// this line will only be instantiated if there are further
// arguments. if rest... is empty, there will be no call to
// print_all(os).
print_all(os, rest...);
}
}

Section 3.3: Iterating with std::integer_sequence


Since C++14, the standard provides the class template

template <class T, T... Ints>


class integer_sequence;

template <std::size_t... Ints>


using index_sequence = std::integer_sequence<std::size_t, Ints...>;

and a generating metafunction for it:

template <class T, T N>


using make_integer_sequence = std::integer_sequence<T, /* a sequence 0, 1, 2, ..., N-1 */ >;

template<std::size_t N>
using make_index_sequence = make_integer_sequence<std::size_t, N>;

While this comes standard in C++14, this can be implemented using C++11 tools.

We can use this tool to call a function with a std::tuple of arguments (standardized in C++17 as std::apply):

namespace detail {
template <class F, class Tuple, std::size_t... Is>
decltype(auto) apply_impl(F&& f, Tuple&& tpl, std::index_sequence<Is...> ) {
return std::forward<F>(f)(std::get<Is>(std::forward<Tuple>(tpl))...);
}
}

template <class F, class Tuple>


decltype(auto) apply(F&& f, Tuple&& tpl) {
return detail::apply_impl(std::forward<F>(f),
std::forward<Tuple>(tpl),

Complete C++ Tips & Secrets for Professionals 24


std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>{});
}

// this will print 3


int f(int, char, double);

auto some_args = std::make_tuple(42, 'x', 3.14);


int r = apply(f, some_args); // calls f(42, 'x', 3.14)

Section 3.4: Tag Dispatching


A simple way of selecting between functions at compile time is to dispatch a function to an overloaded pair of
functions that take a tag as one (usually the last) argument. For example, to implement std::advance(), we can
dispatch on the iterator category:

namespace details {
template <class RAIter, class Distance>
void advance(RAIter& it, Distance n, std::random_access_iterator_tag) {
it += n;
}

template <class BidirIter, class Distance>


void advance(BidirIter& it, Distance n, std::bidirectional_iterator_tag) {
if (n > 0) {
while (n--) ++it;
}
else {
while (n++) --it;
}
}

template <class InputIter, class Distance>


void advance(InputIter& it, Distance n, std::input_iterator_tag) {
while (n--) {
++it;
}
}
}

template <class Iter, class Distance>


void advance(Iter& it, Distance n) {
details::advance(it, n,
typename std::iterator_traits<Iter>::iterator_category{} );
}

The std::XY_iterator_tag arguments of the overloaded details::advance functions are unused function
parameters. The actual implementation does not matter (actually it is completely empty). Their only purpose is to
allow the compiler to select an overload based on which tag class details::advance is called with.

In this example, advance uses the iterator_traits<T>::iterator_category metafunction which returns one of
the iterator_tag classes, depending on the actual type of Iter. A default-constructed object of the
iterator_category<Iter>::type then lets the compiler select one of the different overloads of details::advance.
(This function parameter is likely to be completely optimized away, as it is a default-constructed object of an empty
struct and never used.)

Tag dispatching can give you code that's much easier to read than the equivalents using SFINAE and enable_if.

Note: while C++17's if constexpr may simplify the implementation of advance in particular, it is not suitable for open
implementations unlike tag dispatching.

Complete C++ Tips & Secrets for Professionals 25


Section 3.5: Detect Whether Expression is Valid
It is possible to detect whether an operator or function can be called on a type. To test if a class has an overload of
std::hash, one can do this:

#include <functional> // for std::hash


#include <type_traits> // for std::false_type and std::true_type
#include <utility> // for std::declval

template<class, class = void>


struct has_hash
: std::false_type
{};

template<class T>
struct has_hash<T, decltype(std::hash<T>()(std::declval<T>()), void())>
: std::true_type
{};
Version≥C++17

Since C++17, std::void_t can be used to simplify this type of construct

#include <functional> // for std::hash


#include <type_traits> // for std::false_type, std::true_type, std::void_t
#include <utility> // for std::declval

template<class, class = std::void_t<> >


struct has_hash
: std::false_type
{};

template<class T>
struct has_hash<T, std::void_t< decltype(std::hash<T>()(std::declval<T>())) > >
: std::true_type
{};

where std::void_t is defined as:

template< class... > using void_t = void;

For detecting if an operator, such as operator< is defined, the syntax is almost the same:

template<class, class = void>


struct has_less_than
: std::false_type
{};

template<class T>
struct has_less_than<T, decltype(std::declval<T>() < std::declval<T>(), void())>
: std::true_type
{};

These can be used to use a std::unordered_map<T> if T has an overload for std::hash, but otherwise attempt to
use a std::map<T>:

template <class K, class V>


using hash_invariant_map = std::conditional_t<
has_hash<K>::value,
std::unordered_map<K, V>,
std::map<K,V>>;

Complete C++ Tips & Secrets for Professionals 26


Section 3.6: If-then-else
Version≥C++11

The type std::conditional in the standard library header <type_traits> can select one type or the other, based
on a compile-time boolean value:

template<typename T>
struct ValueOrPointer
{
typename std::conditional<(sizeof(T) > sizeof(void*)), T*, T>::type vop;
};

This struct contains a pointer to T if T is larger than the size of a pointer, or T itself if it is smaller or equal to a
pointer's size. Therefore sizeof(ValueOrPointer) will always be <= sizeof(void*).

Section 3.7: Manual distinction of types when given any type T


When implementing SFINAE using std::enable_if, it is often useful to have access to helper templates that
determines if a given type T matches a set of criteria.

To help us with that, the standard already provides two types analog to true and false which are std::true_type
and std::false_type.

The following example show how to detect if a type T is a pointer or not, the is_pointer template mimic the
behavior of the standard std::is_pointer helper:

template <typename T>


struct is_pointer_: std::false_type {};

template <typename T>


struct is_pointer_<T*>: std::true_type {};

template <typename T>


struct is_pointer: is_pointer_<typename std::remove_cv<T>::type> { }

There are three steps in the above code (sometimes you only need two):

1. The first declaration of is_pointer_ is the default case, and inherits from std::false_type. The default case
should always inherit from std::false_type since it is analogous to a "false condition".

2. The second declaration specialize the is_pointer_ template for pointer T* without caring about what T is
really. This version inherits from std::true_type.

3. The third declaration (the real one) simply remove any unnecessary information from T (in this case we
remove const and volatile qualifiers) and then fall backs to one of the two previous declarations.

Since is_pointer<T> is a class, to access its value you need to either:

Use ::value, e.g. is_pointer<int>::value – value is a static class member of type bool inherited from
std::true_type or std::false_type;
Construct an object of this type, e.g. is_pointer<int>{} – This works because std::is_pointer inherits its
default constructor from std::true_type or std::false_type (which have constexpr constructors) and both
std::true_type and std::false_type have constexpr conversion operators to bool.

Complete C++ Tips & Secrets for Professionals 27


It is a good habit to provides "helper helper templates" that let you directly access the value:

template <typename T>


constexpr bool is_pointer_v = is_pointer<T>::value;
Version≥C++17

In C++17 and above, most helper templates already provide a _v version, e.g.:

template< class T > constexpr bool is_pointer_v = is_pointer<T>::value;


template< class T > constexpr bool is_reference_v = is_reference<T>::value;

Section 3.8: Calculating power with C++11 (and higher)


With C++11 and higher calculations at compile time can be much easier. For example calculating the power of a
given number at compile time will be following:

template <typename T>


constexpr T calculatePower(T value, unsigned power) {
return power == 0 ? 1 : value * calculatePower(value, power-1);
}

Keyword constexpr is responsible for calculating function in compilation time, then and only then, when all the
requirements for this will be met (see more at constexpr keyword reference) for example all the arguments must
be known at compile time.

Note: In C++11 constexpr function must compose only from one return statement.

Advantages: Comparing this to the standard way of compile time calculation, this method is also useful for runtime
calculations. It means, that if the arguments of the function are not known at the compilation time (e.g. value and
power are given as input via user), then function is run in a compilation time, so there's no need to duplicate a code
(as we would be forced in older standards of C++).

E.g.

void useExample() {
constexpr int compileTimeCalculated = calculatePower(3, 3); // computes at compile time,
// as both arguments are known at compilation time
// and used for a constant expression.
int value;
std::cin >> value;
int runtimeCalculated = calculatePower(value, 3); // runtime calculated,
// because value is known only at runtime.
}
Version≥C++17

Another way to calculate power at compile time can make use of fold expression as follows:

#include <iostream>
#include <utility>

template <class T, T V, T N, class I = std::make_integer_sequence<T, N>>


struct power;

template <class T, T V, T N, T... Is>


struct power<T, V, N, std::integer_sequence<T, Is...>> {
static constexpr T value = (static_cast<T>(1) * ... * (V * static_cast<bool>(Is + 1)));
};

int main() {
std::cout << power<int, 4, 2>::value << std::endl;

Complete C++ Tips & Secrets for Professionals 28


}

Section 3.9: Generic Min/Max with variable argument count


Version>C++11

It's possible to write a generic function (for example min) which accepts various numerical types and arbitrary
argument count by template meta-programming. This function declares a min for two arguments and recursively
for more.

template <typename T1, typename T2>


auto min(const T1 &a, const T2 &b)
-> typename std::common_type<const T1&, const T2&>::type
{
return a < b ? a : b;
}

template <typename T1, typename T2, typename ... Args>


auto min(const T1 &a, const T2 &b, const Args& ... args)
-> typename std::common_type<const T1&, const T2&, const Args& ...>::type
{
return min(min(a, b), args...);
}

auto minimum = min(4, 5.8f, 3, 1.8, 3, 1.1, 9);

Chapter 4: Iterators
Section 4.1: Overview
Iterators are Positions

Iterators are a means of navigating and operating on a sequence of elements and are a generalized extension of
pointers. Conceptually it is important to remember that iterators are positions, not elements. For example, take the
following sequence:

A B C

The sequence contains three elements and four positions

+---+---+---+---+
| A | B | C | |
+---+---+---+---+

Elements are things within a sequence. Positions are places where meaningful operations can happen to the
sequence. For example, one inserts into a position, before or after element A, not into an element. Even deletion of
an element (erase(A)) is done by first finding its position, then deleting it.

From Iterators to Values

To convert from a position to a value, an iterator is dereferenced:

auto my_iterator = my_vector.begin(); // position


auto my_value = *my_iterator; // value

One can think of an iterator as dereferencing to the value it refers to in the sequence. This is especially useful in
understanding why you should never dereference the end() iterator in a sequence:

+---+---+---+---+

Complete C++ Tips & Secrets for Professionals 29


| A | B | C | |
+---+---+---+---+
? ?
| +-- An iterator here has no value. Do not dereference it!
+-------------- An iterator here dereferences to the value A.

In all the sequences and containers found in the C++ standard library, begin() will return an iterator to the first
position, and end() will return an iterator to one past the last position (not the last position!). Consequently, the
names of these iterators in algorithms are oftentimes labelled first and last:

+---+---+---+---+
| A | B | C | |
+---+---+---+---+
? ?
| |
+- first +- last

It is also possible to obtain an iterator to any sequence, because even an empty sequence contains at least one
position:

+---+
| |
+---+

In an empty sequence, begin() and end() will be the same position, and neither can be dereferenced:

+---+
| |
+---+
?
|
+- empty_sequence.begin()
|
+- empty_sequence.end()

The alternative visualization of iterators is that they mark the positions between elements:

+---+---+---+
| A | B | C |
+---+---+---+
? ^ ^ ?
| |
+- first +- last

and dereferencing an iterator returns a reference to the element coming after the iterator. Some situations where
this view is particularly useful are:

insert operations will insert elements into the position indicated by the iterator,
erase operations will return an iterator corresponding to the same position as the one passed in,
an iterator and its corresponding reverse iterator are located in the same .position between elements

Invalid Iterators

An iterator becomes invalidated if (say, in the course of an operation) its position is no longer a part of a sequence.
An invalidated iterator cannot be dereferenced until it has been reassigned to a valid position. For example:

std::vector<int>::iterator first;
{
std::vector<int> foo;
first = foo.begin(); // first is now valid

Complete C++ Tips & Secrets for Professionals 30


} // foo falls out of scope and is destroyed
// At this point first is now invalid

The many algorithms and sequence member functions in the C++ standard library have rules governing when
iterators are invalidated. Each algorithm is different in the way they treat (and invalidate) iterators.

Navigating with Iterators

As we know, iterators are for navigating sequences. In order to do that an iterator must migrate its position
throughout the sequence. Iterators can advance forward in the sequence and some can advance backwards:

auto first = my_vector.begin();


++first; // advance the iterator 1 position
std::advance(first, 1); // advance the iterator 1 position
first = std::next(first); // returns iterator to the next element
std::advance(first, -1); // advance the iterator 1 position backwards
first = std::next(first, 20); // returns iterator to the element 20 position
forward
first = std::prev(first, 5); // returns iterator to the element 5 position
backward
auto dist = std::distance(my_vector.begin(), first); // returns distance between two iterators.

Note, second argument of std::distance should be reachable from the first one(or, in other words first should be
less or equal than second).

Even though you can perform arithmetic operators with iterators, not all operations are defined for all types of
iterators. a = b + 3; would work for Random Access Iterators, but wouldn't work for Forward or Bidirectional
Iterators, which still can be advanced by 3 position with something like b = a; ++b; ++b; ++b;. So it is
recommended to use special functions in case you are not sure what is iterator type (for example, in a template
function accepting iterator).

Iterator Concepts

The C++ standard describes several different iterator concepts. These are grouped according to how they behave in
the sequences they refer to. If you know the concept an iterator models (behaves like), you can be assured of the
behavior of that iterator regardless of the sequence to which it belongs. They are often described in order from the
most to least restrictive (because the next iterator concept is a step better than its predecessor):

Input Iterators : Can be dereferenced only once per position. Can only advance, and only one position at a
time.
Forward Iterators : An input iterator that can be dereferenced any number of times.
Bidirectional Iterators : A forward iterator that can also advance backwards one position at a time.
Random Access Iterators : A bidirectional iterator that can advance forwards or backwards any number of
positions at a time.
Contiguous Iterators (since C++17) : A random access iterator that guaranties that underlying data is
contiguous in memory.

Algorithms can vary depending on the concept modeled by the iterators they are given. For example, although
random_shuffle can be implemented for forward iterators, a more efficient variant that requires random access
iterators could be provided.

Iterator traits

Iterator traits provide uniform interface to the properties of iterators. They allow you to retrieve value, difference,
pointer, reference types and also category of iterator:

Complete C++ Tips & Secrets for Professionals 31


template<class Iter>
Iter find(Iter first, Iter last, typename std::iterator_traits<Iter>::value_type val) {
while (first != last) {
if (*first == val)
return first;
++first;
}
return last;
}

Category of iterator can be used to specialize algorithms:

template<class BidirIt>
void test(BidirIt a, std::bidirectional_iterator_tag) {
std::cout << "Bidirectional iterator is used" << std::endl;
}

template<class ForwIt>
void test(ForwIt a, std::forward_iterator_tag) {
std::cout << "Forward iterator is used" << std::endl;
}

template<class Iter>
void test(Iter a) {
test(a, typename std::iterator_traits<Iter>::iterator_category());
}

Categories of iterators are basically iterators concepts, except Contiguous Iterators don't have their own tag, since it
was found to break code.

Section 4.2: Vector Iterator


begin returns an iterator to the first element in the sequence container.

end returns an iterator to the first element past the end.

If the vector object is const, both begin and end return a const_iterator. If you want a const_iterator to be
returned even if your vector is not const, you can use cbegin and cend.

Example:

#include <vector>
#include <iostream>

int main() {
std::vector<int> v = { 1, 2, 3, 4, 5 }; //intialize vector using an initializer_list

for (std::vector<int>::iterator it = v.begin(); it != v.end(); ++it) {


std::cout << *it << " ";
}

return 0;
}

Output:

12345

Section 4.3: Map Iterator


An iterator to the first element in the container.

Complete C++ Tips & Secrets for Professionals 32


If a map object is const-qualified, the function returns a const_iterator. Otherwise, it returns an iterator.

// Create a map and insert some values


std::map<char,int> mymap;
mymap['b'] = 100;
mymap['a'] = 200;
mymap['c'] = 300;

// Iterate over all tuples


for (std::map<char,int>::iterator it = mymap.begin(); it != mymap.end(); ++it)
std::cout << it->first << " => " << it->second << '\n';

Output:

a => 200
b => 100
c => 300

Section 4.4: Reverse Iterators


If we want to iterate backwards through a list or vector we can use a reverse_iterator. A reverse iterator is made
from a bidirectional, or random access iterator which it keeps as a member which can be accessed through base().

To iterate backwards use rbegin() and rend() as the iterators for the end of the collection, and the start of the
collection respectively.

For instance, to iterate backwards use:

std::vector<int> v{1, 2, 3, 4, 5};


for (std::vector<int>::reverse_iterator it = v.rbegin(); it != v.rend(); ++it)
{
cout << *it;
} // prints 54321

A reverse iterator can be converted to a forward iterator via the base() member function. The relationship is that
the reverse iterator references one element past the base() iterator:

std::vector<int>::reverse_iterator r = v.rbegin();
std::vector<int>::iterator i = r.base();
assert(&*r == &*(i-1)); // always true if r, (i-1) are dereferenceable
// and are not proxy iterators

+---+---+---+---+---+---+---+
| | 1 | 2 | 3 | 4 | 5 | |
+---+---+---+---+---+---+---+
? ? ? ?
| | | |
rend() | rbegin() end()
| rbegin().base()
begin()
rend().base()

In the visualization where iterators mark positions between elements, the relationship is simpler:

+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 |
+---+---+---+---+---+
? ?
| |
| end()

Complete C++ Tips & Secrets for Professionals 33


| rbegin()
begin() rbegin().base()
rend()
rend().base()

Section 4.5: Stream Iterators


Stream iterators are useful when we need to read a sequence or print formatted data from a container:

// Data stream. Any number of various whitespace characters will be OK.


std::istringstream istr("1\t 2 3 4");
std::vector<int> v;

// Constructing stream iterators and copying data from stream into vector.
std::copy(
// Iterator which will read stream data as integers.
std::istream_iterator<int>(istr),
// Default constructor produces end-of-stream iterator.
std::istream_iterator<int>(),
std::back_inserter(v));

// Print vector contents.


std::copy(v.begin(), v.end(),
//Will print values to standard output as integers delimeted by " -- ".
std::ostream_iterator<int>(std::cout, " -- "));

The example program will print 1 -- 2 -- 3 -- 4 -- to standard output.

Section 4.6: C Iterators (Pointers)


// This creates an array with 5 values.
const int array[] = { 1, 2, 3, 4, 5 };

#ifdef BEFORE_CPP11

// You can use `sizeof` to determine how many elements are in an array.
const int* first = array;
const int* afterLast = first + sizeof(array) / sizeof(array[0]);

// Then you can iterate over the array by incrementing a pointer until
// it reaches past the end of our array.
for (const int* i = first; i < afterLast; ++i) {
std::cout << *i << std::endl;
}

#else

// With C++11, you can let the STL compute the start and end iterators:
for (auto i = std::begin(array); i != std::end(array); ++i) {
std::cout << *i << std::endl;
}

#endif

This code would output the numbers 1 through 5, one on each line like this:

1
2
3
4
5

Complete C++ Tips & Secrets for Professionals 34


Breaking It Down
const int array[] = { 1, 2, 3, 4, 5 };

This line creates a new integer array with 5 values. C arrays are just pointers to memory where each value is stored
together in a contiguous block.

const int* first = array;


const int* afterLast = first + sizeof(array) / sizeof(array[0]);

These lines create two pointers. The first pointer is given the value of the array pointer, which is the address of the
first element in the array. The sizeof operator when used on a C array returns the size of the array in bytes.
Divided by the size of an element this gives the number of elements in the array. We can use this to find the
address of the block after the array.

for (const int* i = first; i < afterLast; ++i) {

Here we create a pointer which we will use as an iterator. It is initialized with the address of the first element we
want to iterate over, and we'll continue to iterate as long as i is less than afterLast, which means as long as i is
pointing to an address within array.

std::cout << *i << std::endl;

Finally, within the loop we can access the value our iterator i is pointing to by dereferencing it. Here the
dereference operator * returns the value at the address in i.

Section 4.7: Write your own generator-backed iterator


A common pattern in other languages is having a function that produces a "stream" of objects, and being able to
use loop-code to loop over it.

We can model this in C++ as

template<class T>
struct generator_iterator {
using difference_type=std::ptrdiff_t;
using value_type=T;
using pointer=T*;
using reference=T;
using iterator_category=std::input_iterator_tag;
std::optional<T> state;
std::function< std::optional<T>() > operation;
// we store the current element in "state" if we have one:
T operator*() const {
return *state;
}
// to advance, we invoke our operation. If it returns a nullopt
// we have reached the end:
generator_iterator& operator++() {
state = operation();
return *this;
}
generator_iterator operator++(int) {
auto r = *this;
++(*this);
return r;
}
// generator iterators are only equal if they are both in the "end" state:
friend bool operator==( generator_iterator const& lhs, generator_iterator const& rhs ) {
if (!lhs.state && !rhs.state) return true;
return false;

Complete C++ Tips & Secrets for Professionals 35


}
friend bool operator!=( generator_iterator const& lhs, generator_iterator const& rhs ) {
return !(lhs==rhs);
}
// We implicitly construct from a std::function with the right signature:
generator_iterator( std::function< std::optional<T>() > f ):operation(std::move(f))
{
if (operation)
state = operation();
}
// default all special member functions:
generator_iterator( generator_iterator && ) =default;
generator_iterator( generator_iterator const& ) =default;
generator_iterator& operator=( generator_iterator && ) =default;
generator_iterator& operator=( generator_iterator const& ) =default;
generator_iterator() =default;
};

live example.

We store the generated element early so we can more easily detect if we are already at the end.

As the function of an end generator iterator is never used, we can create a range of generator iterators by only
copying the std::function once. A default constructed generator iterator compares equal to itself, and to all other
end-generator-iterators.

Chapter 5: Returning several values from a function


There are many situations where it is useful to return several values from a function: for example, if you want to
input an item and return the price and number in stock, this functionality could be useful. There are many ways to
do this in C++, and most involve the STL. However, if you wish to avoid the STL for some reason, there are still
several ways to do this, including structs/classes and arrays.

Section 5.1: Using std::tuple


Version≥C++11

The type std::tuple can bundle any number of values, potentially including values of different types, into a single
return object:

std::tuple<int, int, int, int> foo(int a, int b) { // or auto (C++14)


return std::make_tuple(a + b, a - b, a * b, a / b);
}

In C++17, a braced initializer list can be used:

Version≥C++17
std::tuple<int, int, int, int> foo(int a, int b) {
return {a + b, a - b, a * b, a / b};
}

Retrieving values from the returned tuple can be cumbersome, requiring the use of the std::get template
function:

auto mrvs = foo(5, 12);


auto add = std::get<0>(mrvs);
auto sub = std::get<1>(mrvs);
auto mul = std::get<2>(mrvs);
auto div = std::get<3>(mrvs);

Complete C++ Tips & Secrets for Professionals 36


If the types can be declared before the function returns, then std::tie can be employed to unpack a tuple into
existing variables:

int add, sub, mul, div;


std::tie(add, sub, mul, div) = foo(5, 12);

If one of the returned values is not needed, std::ignore can be used:

std::tie(add, sub, std::ignore, div) = foo(5, 12);


Version≥C++17

Structured bindings can be used to avoid std::tie:

auto [add, sub, mul, div] = foo(5,12);

If you want to return a tuple of lvalue references instead of a tuple of values, use std::tie in place of
std::make_tuple.

std::tuple<int&, int&> minmax( int& a, int& b ) {


if (b<a)
return std::tie(b,a);
else
return std::tie(a,b);
}

which permits

void increase_least(int& a, int& b) {


std::get<0>(minmax(a,b))++;
}

In some rare cases you'll use std::forward_as_tuple instead of std::tie; be careful if you do so, as temporaries
may not last long enough to be consumed.

Section 5.2: Structured Bindings


Version≥C++17

C++17 introduces structured bindings, which makes it even easier to deal with multiple return types, as you do not
need to rely upon std::tie() or do any manual tuple unpacking:

std::map<std::string, int> m;

// insert an element into the map and check if insertion succeeded


auto [iterator, success] = m.insert({"Hello", 42});

if (success) {
// your code goes here
}

// iterate over all elements without having to use the cryptic 'first' and 'second' names
for (auto const& [key, value] : m) {
std::cout << "The value for " << key << " is " << value << '\n';
}

Structured bindings can be used by default with std::pair, std::tuple, and any type whose non-static data
members are all either public direct members or members of an unambiguous base class:

struct A { int x; };
struct B : A { int y; };

Complete C++ Tips & Secrets for Professionals 37


B foo();

// with structured bindings


const auto [x, y] = foo();

// equivalent code without structured bindings


const auto result = foo();
auto& x = result.x;
auto& y = result.y;

If you make your type "tuple-like" it will also automatically work with your type. A tuple-like is a type with
appropriate tuple_size, tuple_element and get written:

namespace my_ns {
struct my_type {
int x;
double d;
std::string s;
};
struct my_type_view {
my_type* ptr;
};
}

namespace std {
template<>
struct tuple_size<my_ns::my_type_view> : std::integral_constant<std::size_t, 3>
{};

template<> struct tuple_element<my_ns::my_type_view, 0>{ using type = int; };


template<> struct tuple_element<my_ns::my_type_view, 1>{ using type = double; };
template<> struct tuple_element<my_ns::my_type_view, 2>{ using type = std::string; };
}

namespace my_ns {
template<std::size_t I>
decltype(auto) get(my_type_view const& v) {
if constexpr (I == 0)
return v.ptr->x;
else if constexpr (I == 1)
return v.ptr->d;
else if constexpr (I == 2)
return v.ptr->s;
static_assert(I < 3, "Only 3 elements");
}
}

now this works:

my_ns::my_type t{1, 3.14, "hello world"};

my_ns::my_type_view foo() {
return {&t};
}

int main() {
auto[x, d, s] = foo();
std::cout << x << ',' << d << ',' << s << '\n';
}

Complete C++ Tips & Secrets for Professionals 38


Section 5.3: Using struct
A struct can be used to bundle multiple return values:

Version≥C++11
struct foo_return_type {
int add;
int sub;
int mul;
int div;
};

foo_return_type foo(int a, int b) {


return {a + b, a - b, a * b, a / b};
}

auto calc = foo(5, 12);


Version<C++11

Instead of assignment to individual fields, a constructor can be used to simplify the constructing of returned values:

struct foo_return_type {
int add;
int sub;
int mul;
int div;
foo_return_type(int add, int sub, int mul, int div)
: add(add), sub(sub), mul(mul), div(div) {}
};

foo_return_type foo(int a, int b) {


return foo_return_type(a + b, a - b, a * b, a / b);
}

foo_return_type calc = foo(5, 12);

The individual results returned by the function foo() can be retrieved by accessing the member variables of the
struct calc:

std::cout << calc.add << ' ' << calc.sub << ' ' << calc.mul << ' ' << calc.div << '\n';

Output:

17 -7 60 0

Note: When using a struct, the returned values are grouped together in a single object and accessible using
meaningful names. This also helps to reduce the number of extraneous variables created in the scope of the
returned values.

Version≥C++17

In order to unpack a struct returned from a function, structured bindings can be used. This places the out-
parameters on an even footing with the in-parameters:

int a=5, b=12;


auto[add, sub, mul, div] = foo(a, b);
std::cout << add << ' ' << sub << ' ' << mul << ' ' << div << '\n';

The output of this code is identical to that above. The struct is still used to return the values from the function.
This permits you do deal with the fields individually.

Complete C++ Tips & Secrets for Professionals 39


Section 5.4: Using Output Parameters
Parameters can be used for returning one or more values; those parameters are required to be non-const pointers
or references.

References:

void calculate(int a, int b, int& c, int& d, int& e, int& f) {


c = a + b;
d = a - b;
e = a * b;
f = a / b;
}

Pointers:

void calculate(int a, int b, int* c, int* d, int* e, int* f) {


*c = a + b;
*d = a - b;
*e = a * b;
*f = a / b;
}

Some libraries or frameworks use an empty 'OUT' #define to make it abundantly obvious which parameters are
output parameters in the function signature. This has no functional impact, and will be compiled out, but makes the
function signature a bit clearer;

#define OUT

void calculate(int a, int b, OUT int& c) {


c = a + b;
}

Section 5.5: Using a Function Object Consumer


We can provide a consumer that will be called with the multiple relevant values:

Version≥C++11
template <class F>
void foo(int a, int b, F consumer) {
consumer(a + b, a - b, a * b, a / b);
}

// use is simple... ignoring some results is possible as well


foo(5, 12, [](int sum, int , int , int ){
std::cout << "sum is " << sum << '\n';
});

This is known as "continuation passing style".

You can adapt a function returning a tuple into a continuation passing style function via:

Version≥C++17
template<class Tuple>
struct continuation {
Tuple t;
template<class F>
decltype(auto) operator->*(F&& f)&&{
return std::apply( std::forward<F>(f), std::move(t) );
}
};

Complete C++ Tips & Secrets for Professionals 40


std::tuple<int,int,int,int> foo(int a, int b);

continuation(foo(5,12))->*[](int sum, auto&&...) {


std::cout << "sum is " << sum << '\n';
};

with more complex versions being writable in C++14 or C++11.

Section 5.6: Using std::pair


The struct template std::pair can bundle together exactly two return values, of any two types:

#include <utility>
std::pair<int, int> foo(int a, int b) {
return std::make_pair(a+b, a-b);
}

With C++11 or later, an initializer list can be used instead of std::make_pair:

Version≥C++11
#include <utility>
std::pair<int, int> foo(int a, int b) {
return {a+b, a-b};
}

The individual values of the returned std::pair can be retrieved by using the pair's first and second member
objects:

std::pair<int, int> mrvs = foo(5, 12);


std::cout << mrvs.first + mrvs.second << std::endl;

Output:

10

Section 5.7: Using std::array


Version≥C++11

The container std::array can bundle together a fixed number of return values. This number has to be known at
compile-time and all return values have to be of the same type:

std::array<int, 4> bar(int a, int b) {


return { a + b, a - b, a * b, a / b };
}

This replaces c style arrays of the form int bar[4]. The advantage being that various c++ std functions can now be
used on it. It also provides useful member functions like at which is a safe member access function with bound
checking, and size which allows you to return the size of the array without calculation.

Section 5.8: Using Output Iterator


Several values of the same type can be returned by passing an output iterator to the function. This is particularly
common for generic functions (like the algorithms of the standard library).

Example:

template<typename Incrementable, typename OutputIterator>


void generate_sequence(Incrementable from, Incrementable to, OutputIterator output) {

Complete C++ Tips & Secrets for Professionals 41


for (Incrementable k = from; k != to; ++k)
*output++ = k;
}

Example usage:

std::vector<int> digits;
generate_sequence(0, 10, std::back_inserter(digits));
// digits now contains {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

Section 5.9: Using std::vector


A std::vector can be useful for returning a dynamic number of variables of the same type. The following example
uses int as data type, but a std::vector can hold any type that is trivially copyable:

#include <vector>
#include <iostream>

// the following function returns all integers between and including 'a' and 'b' in a vector
// (the function can return up to std::vector::max_size elements with the vector, given that
// the system's main memory can hold that many items)
std::vector<int> fillVectorFrom(int a, int b) {
std::vector<int> temp;
for (int i = a; i <= b; i++) {
temp.push_back(i);
}
return temp;
}

int main() {
// assigns the filled vector created inside the function to the new vector 'v'
std::vector<int> v = fillVectorFrom(1, 10);

// prints "1 2 3 4 5 6 7 8 9 10 "


for (int i = 0; i < v.size(); i++) {
std::cout << v[i] << " ";
}
std::cout << std::endl;
return 0;
}

Chapter 6: std::string
// Empty string declaration

std::string s;

// Constructing from const char* (c-string)

std::string s("Hello");

std::string s = "Hello";

// Constructing using copy constructor

std::string s1("Hello");

Complete C++ Tips & Secrets for Professionals 42


std::string s2(s1);

// Constructing from substring

std::string s1("Hello");

std::string s2(s1, 0, 4); // Copy 4 characters from position 0 of s1 into s2

// Constructing from a buffer of characters

std::string s1("Hello World");


std::string s2(s1, 5); // Copy first 5 characters of s1 into s2

// Construct using fill constructor (char only)

std::string s(5, 'a'); // s contains aaaaa

// Construct using range constructor and iterator

std::string s1("Hello World");

std::string s2(s1.begin(), s1.begin()+5); // Copy first 5 characters of s1 into s2

Strings are objects that represent sequences of characters. The standard string class provides a simple, safe and
versatile alternative to using explicit arrays of chars when dealing with text and other sequences of characters. The
C++ string class is part of the std namespace and was standardized in 1998.

Section 6.1: Tokenize


Listed from least expensive to most expensive at run-time:

1. str::strtok is the cheapest standard provided tokenization method, it also allows the delimiter to be
modified between tokens, but it incurs 3 difficulties with modern C++:

std::strtok cannot be used on multiple strings at the same time (though some implementations do
extend to support this, such as: strtok_s)
For the same reason std::strtok cannot be used on multiple threads simultaneously (this may
however be implementation defined, for example: Visual Studio's implementation is thread safe)
Calling std::strtok modifies the std::string it is operating on, so it cannot be used on const
strings, const char*s, or literal strings, to tokenize any of these with std::strtok or to operate on a
std::string who's contents need to be preserved, the input would have to be copied, then the copy
could be operated on

Generally any of these options cost will be hidden in the allocation cost of the tokens, but if the cheapest
algorithm is required and std::strtok's difficulties are not overcomable consider a hand-spun solution.

// String to tokenize
std::string str{ "The quick brown fox" };
// Vector to store tokens
vector<std::string> tokens;

Complete C++ Tips & Secrets for Professionals 43


for (auto i = strtok(&str[0], " "); i != NULL; i = strtok(NULL, " "))
tokens.push_back(i);

Live Example

2. The std::istream_iterator uses the stream's extraction operator iteratively. If the input std::string is
white-space delimited this is able to expand on the std::strtok option by eliminating its difficulties, allowing
inline tokenization thereby supporting the generation of a const vector<string>, and by adding support for
multiple delimiting white-space character:

// String to tokenize
const std::string str("The quick \tbrown \nfox");
std::istringstream is(str);
// Vector to store tokens
const std::vector<std::string> tokens = std::vector<std::string>(
std::istream_iterator<std::string>(is),
std::istream_iterator<std::string>());

Live Example

3. The std::regex_token_iterator uses a std::regex to iteratively tokenize. It provides for a more flexible
delimiter definition. For example, non-delimited commas and white-space:

Version≥C++11
// String to tokenize
const std::string str{ "The ,qu\\,ick ,\tbrown, fox" };
const std::regex re{ "\\s*((?:[^\\\\,]|\\\\.)*?)\\s*(?:,|$)" };
// Vector to store tokens
const std::vector<std::string> tokens{
std::sregex_token_iterator(str.begin(), str.end(), re, 1),
std::sregex_token_iterator()
};

Live Example

See the regex_token_iterator Example for more details.

Section 6.2: Conversion to (const) char*


In order to get const char* access to the data of a std::string you can use the string's c_str() member function.
Keep in mind that the pointer is only valid as long as the std::string object is within scope and remains
unchanged, that means that only const methods may be called on the object.

Version≥C++17

The data() member function can be used to obtain a modifiable char*, which can be used to manipulate the
std::string object's data.

Version≥C++11

A modifiable char* can also be obtained by taking the address of the first character: &s[0]. Within C++11, this is
guaranteed to yield a well-formed, null-terminated string. Note that &s[0] is well-formed even if s is empty,
whereas &s.front() is undefined if s is empty.

Version≥C++11
std::string str("This is a string.");
const char* cstr = str.c_str(); // cstr points to: "This is a string.\0"
const char* data = str.data(); // data points to: "This is a string.\0"
std::string str("This is a string.");

Complete C++ Tips & Secrets for Professionals 44


// Copy the contents of str to untie lifetime from the std::string object
std::unique_ptr<char []> cstr = std::make_unique<char[]>(str.size() + 1);

// Alternative to the line above (no exception safety):


// char* cstr_unsafe = new char[str.size() + 1];

std::copy(str.data(), str.data() + str.size(), cstr);


cstr[str.size()] = '\0'; // A null-terminator needs to be added

// delete[] cstr_unsafe;
std::cout << cstr.get();

Section 6.3: Using the std::string_view class


Version≥C++17

C++17 introduces std::string_view, which is simply a non-owning range of const chars, implementable as either
a pair of pointers or a pointer and a length. It is a superior parameter type for functions that requires non-
modifiable string data. Before C++17, there were three options for this:

void foo(std::string const& s); // pre-C++17, single argument, could incur


// allocation if caller's data was not in a string
// (e.g. string literal or vector<char> )

void foo(const char* s, size_t len); // pre-C++17, two arguments, have to pass them
// both everywhere

void foo(const char* s); // pre-C++17, single argument, but need to call
// strlen()

template <class StringT>


void foo(StringT const& s); // pre-C++17, caller can pass arbitrary char data
// provider, but now foo() has to live in a header

All of these can be replaced with:

void foo(std::string_view s); // post-C++17, single argument, tighter coupling


// zero copies regardless of how caller is storing
// the data

Note that std::string_view cannot modify its underlying data.

string_view is useful when you want to avoid unnecessary copies.

It offers a useful subset of the functionality that std::string does, although some of the functions behave
differently:

std::string str = "lllloooonnnngggg sssstttrrriiinnnggg"; //A really long string

//Bad way - 'string::substr' returns a new string (expensive if the string is long)
std::cout << str.substr(15, 10) << '\n';

//Good way - No copies are created!


std::string_view view = str;

// string_view::substr returns a new string_view


std::cout << view.substr(15, 10) << '\n';

Section 6.4: Conversion to std::wstring


In C++, sequences of characters are represented by specializing the std::basic_string class with a native

Complete C++ Tips & Secrets for Professionals 45


character type. The two major collections defined by the standard library are std::string and std::wstring:

std::string is built with elements of type char

std::wstring is built with elements of type wchar_t

To convert between the two types, use wstring_convert:

#include <string>
#include <codecvt>
#include <locale>

std::string input_str = "this is a -string-, which is a sequence based on the -char- type.";
std::wstring input_wstr = L"this is a -wide- string, which is based on the -wchar_t- type.";

// conversion
std::wstring str_turned_to_wstr =
std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(input_str);

std::string wstr_turned_to_str =
std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(input_wstr);

In order to improve usability and/or readability, you can define functions to perform the conversion:

#include <string>
#include <codecvt>
#include <locale>

using convert_t = std::codecvt_utf8<wchar_t>;


std::wstring_convert<convert_t, wchar_t> strconverter;

std::string to_string(std::wstring wstr)


{
return strconverter.to_bytes(wstr);
}

std::wstring to_wstring(std::string str)


{
return strconverter.from_bytes(str);
}

Sample usage:

std::wstring a_wide_string = to_wstring("Hello World!");

That's certainly more readable than std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes("Hello


World!").

Please note that char and wchar_t do not imply encoding, and gives no indication of size in bytes. For instance,
wchar_t is commonly implemented as a 2-bytes data type and typically contains UTF-16 encoded data under
Windows (or UCS-2 in versions prior to Windows 2000) and as a 4-bytes data type encoded using UTF-32 under
Linux. This is in contrast with the newer types char16_t and char32_t, which were introduced in C++11 and are
guaranteed to be large enough to hold any UTF16 or UTF32 "character" (or more precisely, code point) respectively.

Section 6.5: Lexicographical comparison


Two std::strings can be compared lexicographically using the operators ==, !=, <, <=, >, and >=:

Complete C++ Tips & Secrets for Professionals 46


std::string str1 = "Foo";
std::string str2 = "Bar";

assert(!(str1 < str2));


assert(str > str2);
assert(!(str1 <= str2));
assert(str1 >= str2);
assert(!(str1 == str2));
assert(str1 != str2);

All these functions use the underlying std::string::compare() method to perform the comparison, and return for
convenience boolean values. The operation of these functions may be interpreted as follows, regardless of the
actual implementation:

operator==:

If str1.length() == str2.length() and each character pair matches, then returns true, otherwise returns
false.

operator!=:

If str1.length() != str2.length() or one character pair doesn't match, returns true, otherwise it returns
false.

operator< or operator>:

Finds the first different character pair, compares them then returns the boolean result.

operator<= or operator>=:

Finds the first different character pair, compares them then returns the boolean result.

Note: The term character pair means the corresponding characters in both strings of the same positions. For
better understanding, if two example strings are str1 and str2, and their lengths are n and m respectively, then
character pairs of both strings means each str1[i] and str2[i] pairs where i = 0, 1, 2, ..., max(n,m). If for any i
where the corresponding character does not exist, that is, when i is greater than or equal to n or m, it would be
considered as the lowest value.

Here is an example of using <:

std::string str1 = "Barr";


std::string str2 = "Bar";

assert(str2 < str1);

The steps are as follows:

1. Compare the first characters, 'B' == 'B' - move on.


2. Compare the second characters, 'a' == 'a' - move on.
3. Compare the third characters, 'r' == 'r' - move on.
4. The str2 range is now exhausted, while the str1 range still has characters. Thus, str2 < str1.

Complete C++ Tips & Secrets for Professionals 47


Section 6.6: Trimming characters at start/end
This example requires the headers <algorithm>, <locale>, and <utility>.

Version≥C++11

To trim a sequence or string means to remove all leading and trailing elements (or characters) matching a certain
predicate. We first trim the trailing elements, because it doesn't involve moving any elements, and then trim the
leading elements. Note that the generalizations below work for all types of std::basic_string (e.g. std::string
and std::wstring), and accidentally also for sequence containers (e.g. std::vector and std::list).

template <typename Sequence, // any basic_string, vector, list etc.


typename Pred> // a predicate on the element (character) type
Sequence& trim(Sequence& seq, Pred pred) {
return trim_start(trim_end(seq, pred), pred);
}

Trimming the trailing elements involves finding the last element not matching the predicate, and erasing from there
on:

template <typename Sequence, typename Pred>


Sequence& trim_end(Sequence& seq, Pred pred) {
auto last = std::find_if_not(seq.rbegin(),
seq.rend(),
pred);
seq.erase(last.base(), seq.end());
return seq;
}

Trimming the leading elements involves finding the first element not matching the predicate and erasing up to
there:

template <typename Sequence, typename Pred>


Sequence& trim_start(Sequence& seq, Pred pred) {
auto first = std::find_if_not(seq.begin(),
seq.end(),
pred);
seq.erase(seq.begin(), first);
return seq;
}

To specialize the above for trimming whitespace in a std::string we can use the std::isspace() function as a
predicate:

std::string& trim(std::string& str, const std::locale& loc = std::locale()) {


return trim(str, [&loc](const char c){ return std::isspace(c, loc); });
}

std::string& trim_start(std::string& str, const std::locale& loc = std::locale()) {


return trim_start(str, [&loc](const char c){ return std::isspace(c, loc); });
}

std::string& trim_end(std::string& str, const std::locale& loc = std::locale()) {


return trim_end(str, [&loc](const char c){ return std::isspace(c, loc); });
}

Similarly, we can use the std::iswspace() function for std::wstring etc.

If you wish to create a new sequence that is a trimmed copy, then you can use a separate function:

Complete C++ Tips & Secrets for Professionals 48


template <typename Sequence, typename Pred>
Sequence trim_copy(Sequence seq, Pred pred) { // NOTE: passing seq by value
trim(seq, pred);
return seq;
}

Section 6.7: String replacement


Replace by position

To replace a portion of a std::string you can use the method replace from std::string.

replace has a lot of useful overloads:

//Define string
std::string str = "Hello foo, bar and world!";
std::string alternate = "Hello foobar";

//1)
str.replace(6, 3, "bar"); //"Hello bar, bar and world!"

//2)
str.replace(str.begin() + 6, str.end(), "nobody!"); //"Hello nobody!"

//3)
str.replace(19, 5, alternate, 6, 6); //"Hello foo, bar and foobar!"
Version≥C++14
//4)
str.replace(19, 5, alternate, 6); //"Hello foo, bar and foobar!"
//5)
str.replace(str.begin(), str.begin() + 5, str.begin() + 6, str.begin() + 9);
//"foo foo, bar and world!"

//6)
str.replace(0, 5, 3, 'z'); //"zzz foo, bar and world!"

//7)
str.replace(str.begin() + 6, str.begin() + 9, 3, 'x'); //"Hello xxx, bar and world!"
Version≥C++11
//8)
str.replace(str.begin(), str.begin() + 5, { 'x', 'y', 'z' }); //"xyz foo, bar and world!"
Replace occurrences of a string with another string

Replace only the first occurrence of replace with with in str:

std::string replaceString(std::string str,


const std::string& replace,
const std::string& with){
std::size_t pos = str.find(replace);
if (pos != std::string::npos)
str.replace(pos, replace.length(), with);
return str;
}

Replace all occurrence of replace with with in str:

std::string replaceStringAll(std::string str,


const std::string& replace,
const std::string& with) {
if(!replace.empty()) {
std::size_t pos = 0;
while ((pos = str.find(replace, pos)) != std::string::npos) {

Complete C++ Tips & Secrets for Professionals 49


str.replace(pos, replace.length(), with);
pos += with.length();
}
}
return str;
}

Section 6.8: Converting to std::string


std::ostringstream can be used to convert any streamable type to a string representation, by inserting the object
into a std::ostringstream object (with the stream insertion operator <<) and then converting the whole
std::ostringstream to a std::string.

For int for instance:

#include <sstream>

int main()
{
int val = 4;
std::ostringstream str;
str << val;
std::string converted = str.str();
return 0;
}

Writing your own conversion function, the simple:

template<class T>
std::string toString(const T& x)
{
std::ostringstream ss;
ss << x;
return ss.str();
}

works but isn't suitable for performance critical code.

User-defined classes may implement the stream insertion operator if desired:

std::ostream operator<<( std::ostream& out, const A& a )


{
// write a string representation of a to out
return out;
}
Version≥C++11

Aside from streams, since C++11 you can also use the std::to_string (and std::to_wstring) function which is
overloaded for all fundamental types and returns the string representation of its parameter.

std::string s = to_string(0x12f3); // after this the string s contains "4851"

Section 6.9: Splitting


Use std::string::substr to split a string. There are two variants of this member function.

The first takes a starting position from which the returned substring should begin. The starting position must be
valid in the range (0, str.length()]:

std::string str = "Hello foo, bar and world!";


std::string newstr = str.substr(11); // "bar and world!"

Complete C++ Tips & Secrets for Professionals 50


The second takes a starting position and a total length of the new substring. Regardless of the length, the substring
will never go past the end of the source string:

std::string str = "Hello foo, bar and world!";


std::string newstr = str.substr(15, 3); // "and"

Note that you can also call substr with no arguments, in this case an exact copy of the string is returned

std::string str = "Hello foo, bar and world!";


std::string newstr = str.substr(); // "Hello foo, bar and world!"

Section 6.10: Accessing a character


There are several ways to extract characters from a std::string and each is subtly different.

std::string str("Hello world!");


operator[](n)

Returns a reference to the character at index n.

std::string::operator[] is not bounds-checked and does not throw an exception. The caller is responsible for
asserting that the index is within the range of the string:

char c = str[6]; // 'w'


at(n)

Returns a reference to the character at index n.

std::string::at is bounds checked, and will throw std::out_of_range if the index is not within the range of the
string:

char c = str.at(7); // 'o'


Version≥C++11

Note: Both of these examples will result in undefined behavior if the string is empty.

front()

Returns a reference to the first character:

char c = str.front(); // 'H'


back()

Returns a reference to the last character:

char c = str.back(); // '!'

Section 6.11: Checking if a string is a prefix of another


Version≥C++14

In C++14, this is easily done by std::mismatch which returns the first mismatching pair from two ranges:

std::string prefix = "foo";


std::string string = "foobar";

bool isPrefix = std::mismatch(prefix.begin(), prefix.end(),


string.begin(), string.end()).first == prefix.end();

Note that a range-and-a-half version of mismatch() existed prior to C++14, but this is unsafe in the case that the

Complete C++ Tips & Secrets for Professionals 51


second string is the shorter of the two.

Version<C++14

We can still use the range-and-a-half version of std::mismatch(), but we need to first check that the first string is at
most as big as the second:

bool isPrefix = prefix.size() <= string.size() &&


std::mismatch(prefix.begin(), prefix.end(),
string.begin(), string.end()).first == prefix.end();
Version≥C++17

With std::string_view, we can write the direct comparison we want without having to worry about allocation
overhead or making copies:

bool isPrefix(std::string_view prefix, std::string_view full)


{
return prefix == full.substr(0, prefix.size());
}

Section 6.12: Looping through each character


Version≥C++11

std::string supports iterators, and so you can use a ranged based loop to iterate through each character:

std::string str = "Hello World!";


for (auto c : str)
std::cout << c;

You can use a "traditional" for loop to loop through every character:

std::string str = "Hello World!";


for (std::size_t i = 0; i < str.length(); ++i)
std::cout << str[i];

Section 6.13: Conversion to integers/floating point types


A std::string containing a number can be converted into an integer type, or a floating point type, using
conversion functions.

Note that all of these functions stop parsing the input string as soon as they encounter a non-numeric character, so
"123abc" will be converted into 123.

The std::ato* family of functions converts C-style strings (character arrays) to integer or floating-point types:

std::string ten = "10";

double num1 = std::atof(ten.c_str());


int num2 = std::atoi(ten.c_str());
long num3 = std::atol(ten.c_str());
Version≥C++11
long long num4 = std::atoll(ten.c_str());

However, use of these functions is discouraged because they return 0 if they fail to parse the string. This is bad
because 0 could also be a valid result, if for example the input string was "0", so it is impossible to determine if the
conversion actually failed.

The newer std::sto* family of functions convert std::strings to integer or floating-point types, and throw
exceptions if they could not parse their input. You should use these functions if possible:

Complete C++ Tips & Secrets for Professionals 52


Version≥C++11
std::string ten = "10";

int num1 = std::stoi(ten);


long num2 = std::stol(ten);
long long num3 = std::stoll(ten);

float num4 = std::stof(ten);


double num5 = std::stod(ten);
long double num6 = std::stold(ten);

Furthermore, these functions also handle octal and hex strings unlike the std::ato* family. The second parameter
is a pointer to the first unconverted character in the input string (not illustrated here), and the third parameter is
the base to use. 0 is automatic detection of octal (starting with 0) and hex (starting with 0x or 0X), and any other
value is the base to use

std::string ten = "10";


std::string ten_octal = "12";
std::string ten_hex = "0xA";

int num1 = std::stoi(ten, 0, 2); // Returns 2


int num2 = std::stoi(ten_octal, 0, 8); // Returns 10
long num3 = std::stol(ten_hex, 0, 16); // Returns 10
long num4 = std::stol(ten_hex); // Returns 0
long num5 = std::stol(ten_hex, 0, 0); // Returns 10 as it detects the leading 0x

Section 6.14: Concatenation


You can concatenate std::strings using the overloaded + and += operators. Using the + operator:

std::string hello = "Hello";


std::string world = "world";
std::string helloworld = hello + world; // "Helloworld"

Using the += operator:

std::string hello = "Hello";


std::string world = "world";
hello += world; // "Helloworld"

You can also append C strings, including string literals:

std::string hello = "Hello";


std::string world = "world";
const char *comma = ", ";
std::string newhelloworld = hello + comma + world + "!"; // "Hello, world!"

You can also use push_back() to push back individual chars:

std::string s = "a, b, ";


s.push_back('c'); // "a, b, c"

There is also append(), which is pretty much like +=:

std::string app = "test and ";


app.append("test"); // "test and test"

Section 6.15: Converting between character encodings


Converting between encodings is easy with C++11 and most compilers are able to deal with it in a cross-platform
manner through <codecvt> and <locale> headers.

Complete C++ Tips & Secrets for Professionals 53


#include <iostream>
#include <codecvt>
#include <locale>
#include <string>
using namespace std;

int main() {
// converts between wstring and utf8 string
wstring_convert<codecvt_utf8_utf16<wchar_t>> wchar_to_utf8;
// converts between u16string and utf8 string
wstring_convert<codecvt_utf8_utf16<char16_t>, char16_t> utf16_to_utf8;

wstring wstr = L"foobar";


string utf8str = wchar_to_utf8.to_bytes(wstr);
wstring wstr2 = wchar_to_utf8.from_bytes(utf8str);

wcout << wstr << endl;


cout << utf8str << endl;
wcout << wstr2 << endl;

u16string u16str = u"foobar";


string utf8str2 = utf16_to_utf8.to_bytes(u16str);
u16string u16str2 = utf16_to_utf8.from_bytes(utf8str2);

return 0;
}

Mind that Visual Studio 2015 provides supports for these conversion but a bug in their library implementation
requires to use a different template for wstring_convert when dealing with char16_t:

using utf16_char = unsigned short;


wstring_convert<codecvt_utf8_utf16<utf16_char>, utf16_char> conv_utf8_utf16;

void strings::utf16_to_utf8(const std::u16string& utf16, std::string& utf8)


{
std::basic_string<utf16_char> tmp;
tmp.resize(utf16.length());
std::copy(utf16.begin(), utf16.end(), tmp.begin());
utf8 = conv_utf8_utf16.to_bytes(tmp);
}
void strings::utf8_to_utf16(const std::string& utf8, std::u16string& utf16)
{
std::basic_string<utf16_char> tmp = conv_utf8_utf16.from_bytes(utf8);
utf16.clear();
utf16.resize(tmp.length());
std::copy(tmp.begin(), tmp.end(), utf16.begin());
}

Section 6.16: Finding character(s) in a string


To find a character or another string, you can use std::string::find. It returns the position of the first character
of the first match. If no matches were found, the function returns std::string::npos

std::string str = "Curiosity killed the cat";


auto it = str.find("cat");

if (it != std::string::npos)
std::cout << "Found at position: " << it << '\n';
else
std::cout << "Not found!\n";

Complete C++ Tips & Secrets for Professionals 54


Found at position: 21

The search opportunities are further expanded by the following functions:

find_first_of // Find first occurrence of characters


find_first_not_of // Find first absence of characters
find_last_of // Find last occurrence of characters
find_last_not_of // Find last absence of characters

These functions can allow you to search for characters from the end of the string, as well as find the negative case
(ie. characters that are not in the string). Here is an example:

std::string str = "dog dog cat cat";


std::cout << "Found at position: " << str.find_last_of("gzx") << '\n';

Found at position: 6

Note: Be aware that the above functions do not search for substrings, but rather for characters contained in the
search string. In this case, the last occurrence of 'g' was found at position 6 (the other characters weren't found).

Chapter 7: Namespaces
namespace identifier(opt) { declaration-seq }
inline namespace identifier(opt) { declaration-seq } /* since C++11 */
inline(opt) namespace attribute-specifier-seq identifier(opt) { declaration-seq } /* since C++17 */
namespace enclosing-namespace-specifier :: identifier { declaration-seq } /* since C++17 */
namespace identifier = qualified-namespace-specifier;
using namespace nested-name-specifier(opt) namespace-name;
attribute-specifier-seq using namespace nested-name-specifier(opt) namespace-name; /* since C++11 */

Used to prevent name collisions when using multiple libraries, a namespace is a declarative prefix for functions,
classes, types, etc.

Section 7.1: What are namespaces?


A C++ namespace is a collection of C++ entities (functions, classes, variables), whose names are prefixed by the
name of the namespace. When writing code within a namespace, named entities belonging to that namespace
need not be prefixed with the namespace name, but entities outside of it must use the fully qualified name. The
fully qualified name has the format <namespace>::<entity>. Example:

namespace Example
{
const int test = 5;

const int test2 = test + 12; //Works within `Example` namespace


}

const int test3 = test + 3; //Fails; `test` not found outside of namespace.

const int test3 = Example::test + 3; //Works; fully qualified name used.

Namespaces are useful for grouping related definitions together. Take the analogy of a shopping mall. Generally a
shopping mall is split up into several stores, each store selling items from a specific category. One store might sell
electronics, while another store might sell shoes. These logical separations in store types help the shoppers find the
items they're looking for. Namespaces help c++ programmers, like shoppers, find the functions, classes, and
variables they're looking for by organizing them in a logical manner. Example:

Complete C++ Tips & Secrets for Professionals 55


namespace Electronics
{
int TotalStock;
class Headphones
{
// Description of a Headphone (color, brand, model number, etc.)
};
class Television
{
// Description of a Television (color, brand, model number, etc.)
};
}

namespace Shoes
{
int TotalStock;
class Sandal
{
// Description of a Sandal (color, brand, model number, etc.)
};
class Slipper
{
// Description of a Slipper (color, brand, model number, etc.)
};
}

There is a single namespace predefined, which is the global namespace that has no name, but can be denoted by
::. Example:

void bar() {
// defined in global namespace
}
namespace foo {
void bar() {
// defined in namespace foo
}
void barbar() {
bar(); // calls foo::bar()
::bar(); // calls bar() defined in global namespace
}
}

Section 7.2: Argument Dependent Lookup


When calling a function without an explicit namespace qualifier, the compiler can choose to call a function within a
namespace if one of the parameter types to that function is also in that namespace. This is called "Argument
Dependent Lookup", or ADL:

namespace Test
{
int call(int i);

class SomeClass {...};

int call_too(const SomeClass &data);


}

call(5); //Fails. Not a qualified function name.

Test::SomeClass data;

Complete C++ Tips & Secrets for Professionals 56


call_too(data); //Succeeds

call fails because none of its parameter types come from the Test namespace. call_too works because
SomeClass is a member of Test and therefore it qualifies for ADL rules.

When does ADL not occur

ADL does not occur if normal unqualified lookup finds a class member, a function that has been declared at block
scope, or something that is not of function type. For example:

void foo();
namespace N {
struct X {};
void foo(X ) { std::cout << '1'; }
void qux(X ) { std::cout << '2'; }
}

struct C {
void foo() {}
void bar() {
foo(N::X{}); // error: ADL is disabled and C::foo() takes no arguments
}
};

void bar() {
extern void foo(); // redeclares ::foo
foo(N::X{}); // error: ADL is disabled and ::foo() doesn't take any arguments
}

int qux;

void baz() {
qux(N::X{}); // error: variable declaration disables ADL for "qux"
}

Section 7.3: Extending namespaces


A useful feature of namespaces is that you can expand them (add members to it).

namespace Foo
{
void bar() {}
}

//some other stuff

namespace Foo
{
void bar2() {}
}

Section 7.4: Using directive


The keyword 'using' has three flavors. Combined with keyword 'namespace' you write a 'using directive':

If you don't want to write Foo:: in front of every stuff in the namespace Foo, you can use using namespace Foo; to
import every single thing out of Foo.

namespace Foo
{
void bar() {}

Complete C++ Tips & Secrets for Professionals 57


void baz() {}
}

//Have to use Foo::bar()


Foo::bar();

//Import Foo
using namespace Foo;
bar(); //OK
baz(); //OK

It is also possible to import selected entities in a namespace rather than the whole namespace:

using Foo::bar;
bar(); //OK, was specifically imported
baz(); // Not OK, was not imported

A word of caution: using namespaces in header files is seen as bad style in most cases. If this is done, the
namespace is imported in every file that includes the header. Since there is no way of "un-using" a namespace, this
can lead to namespace pollution (more or unexpected symbols in the global namespace) or, worse, conflicts. See
this example for an illustration of the problem:

/***** foo.h *****/


namespace Foo
{
class C;
}

/***** bar.h *****/


namespace Bar
{
class C;
}

/***** baz.h *****/


#include "foo.h"
using namespace Foo;

/***** main.cpp *****/


#include "bar.h"
#include "baz.h"

using namespace Bar;


C c; // error: Ambiguity between Bar::C and Foo::C

A using-directive cannot occur at class scope.

Section 7.5: Making namespaces


Creating a namespace is really easy:

//Creates namespace foo


namespace Foo
{
//Declares function bar in namespace foo
void bar() {}
}

To call bar, you have to specify the namespace first, followed by the scope resolution operator :::

Foo::bar();

Complete C++ Tips & Secrets for Professionals 58


It is allowed to create one namespace in another, for example:

namespace A
{
namespace B
{
namespace C
{
void bar() {}
}
}
}
Version≥C++17

The above code could be simplified to the following:

namespace A::B::C
{
void bar() {}
}

Section 7.6: Unnamed/anonymous namespaces


An unnamed namespace can be used to ensure names have internal linkage (can only be referred to by the current
translation unit). Such a namespace is defined in the same way as any other namespace, but without the name:

namespace {
int foo = 42;
}

foo is only visible in the translation unit in which it appears.

It is recommended to never use unnamed namespaces in header files as this gives a version of the content for
every translation unit it is included in. This is especially important if you define non-const globals.

// foo.h
namespace {
std::string globalString;
}

// 1.cpp
#include "foo.h" //< Generates unnamed_namespace{1.cpp}::globalString ...

globalString = "Initialize";

// 2.cpp
#include "foo.h" //< Generates unnamed_namespace{2.cpp}::globalString ...

std::cout << globalString; //< Will always print the empty string

Section 7.7: Compact nested namespaces


Version≥C++17
namespace a {
namespace b {
template<class T>
struct qualifies : std::false_type {};
}
}

namespace other {
struct bob {};

Complete C++ Tips & Secrets for Professionals 59


}

namespace a::b {
template<>
struct qualifies<::other::bob> : std::true_type {};
}

You can enter both the a and b namespaces in one step with namespace a::b starting in C++17.

Section 7.8: Namespace alias


A namespace can be given an alias (i.e., another name for the same namespace) using the namespace identifier =
syntax. Members of the aliased namespace can be accessed by qualifying them with the name of the alias. In the
following example, the nested namespace AReallyLongName::AnotherReallyLongName is inconvenient to type, so
the function qux locally declares an alias N. Members of that namespace can then be accessed simply using N::.

namespace AReallyLongName {
namespace AnotherReallyLongName {
int foo();
int bar();
void baz(int x, int y);
}
}
void qux() {
namespace N = AReallyLongName::AnotherReallyLongName;
N::baz(N::foo(), N::bar());
}

Section 7.9: Inline namespace


Version≥C++11

inline namespace includes the content of the inlined namespace in the enclosing namespace, so

namespace Outer
{
inline namespace Inner
{
void foo();
}
}

is mostly equivalent to

namespace Outer
{

namespace Inner
{
void foo();
}

using Inner::foo;
}

but element from Outer::Inner:: and those associated into Outer:: are identical.

So following is equivalent

Outer::foo();

Complete C++ Tips & Secrets for Professionals 60


Outer::Inner::foo();

The alternative using namespace Inner; would not be equivalent for some tricky parts as template specialization:

For

#include <outer.h> // See below

class MyCustomType;
namespace Outer
{
template <>
void foo<MyCustomType>() { std::cout << "Specialization"; }
}

The inline namespace allows the specialization of Outer::foo

// outer.h
// include guard omitted for simplification

namespace Outer
{
inline namespace Inner
{
template <typename T>
void foo() { std::cout << "Generic"; }
}
}

Whereas the using namespace doesn't allow the specialization of Outer::foo

// outer.h
// include guard omitted for simplification

namespace Outer
{
namespace Inner
{
template <typename T>
void foo() { std::cout << "Generic"; }
}
using namespace Inner;
// Specialization of `Outer::foo` is not possible
// it should be `Outer::Inner::foo`.
}

Inline namespace is a way to allow several version to cohabit and defaulting to the inline one

namespace MyNamespace
{
// Inline the last version
inline namespace Version2
{
void foo(); // New version
void bar();
}

namespace Version1 // The old one


{
void foo();
}

Complete C++ Tips & Secrets for Professionals 61


}

And with usage

MyNamespace::Version1::foo(); // old version


MyNamespace::Version2::foo(); // new version
MyNamespace::foo(); // default version : MyNamespace::Version1::foo();

Section 7.10: Aliasing a long namespace


This is usually used for renaming or shortening long namespace references such referring to components of a
library.

namespace boost
{
namespace multiprecision
{
class Number ...
}
}

namespace Name1 = boost::multiprecision;

// Both Type declarations are equivalent


boost::multiprecision::Number X // Writing the full namespace path, longer
Name1::Number Y // using the name alias, shorter

Section 7.11: Alias Declaration scope


Alias Declaration are affected by preceding using statements

namespace boost
{
namespace multiprecision
{
class Number ...
}
}

using namespace boost;

// Both Namespace are equivalent


namespace Name1 = boost::multiprecision;
namespace Name2 = multiprecision;

However, it is easier to get confused over which namespace you are aliasing when you have something like this:

namespace boost
{
namespace multiprecision
{
class Number ...
}
}

namespace numeric
{
namespace multiprecision
{
class Number ...

Complete C++ Tips & Secrets for Professionals 62


}
}

using namespace numeric;


using namespace boost;

// Not recommended as
// its not explicitly clear whether Name1 refers to
// numeric::multiprecision or boost::multiprecision
namespace Name1 = multiprecision;

// For clarity, its recommended to use absolute paths


// instead
namespace Name2 = numeric::multiprecision;
namespace Name3 = boost::multiprecision;

Chapter 8: File I/O


C++ file I/O is done via streams. The key abstractions are:

std::istream for reading text.

std::ostream for writing text.

std::streambuf for reading or writing characters.

Formatted input uses operator>>.

Formatted output uses operator<<.

Streams use std::locale, e.g., for details of the formatting and for translation between external encodings and the
internal encoding.

More on streams: <iostream> Library

Section 8.1: Writing to a file


There are several ways to write to a file. The easiest way is to use an output file stream (ofstream) together with the
stream insertion operator (<<):

std::ofstream os("foo.txt");
if(os.is_open()){
os << "Hello World!";
}

Instead of <<, you can also use the output file stream's member function write():

std::ofstream os("foo.txt");
if(os.is_open()){
char data[] = "Foo";

// Writes 3 characters from data -> "Foo".


os.write(data, 3);
}

After writing to a stream, you should always check if error state flag badbit has been set, as it indicates whether the
operation failed or not. This can be done by calling the output file stream's member function bad():

os << "Hello Badbit!"; // This operation might fail for any reason.
if (os.bad())

Complete C++ Tips & Secrets for Professionals 63


// Failed to write!

Section 8.2: Opening a file


Opening a file is done in the same way for all 3 file streams (ifstream, ofstream, and fstream).

You can open the file directly in the constructor:

std::ifstream ifs("foo.txt"); // ifstream: Opens file "foo.txt" for reading only.

std::ofstream ofs("foo.txt"); // ofstream: Opens file "foo.txt" for writing only.

std::fstream iofs("foo.txt"); // fstream: Opens file "foo.txt" for reading and writing.

Alternatively, you can use the file stream's member function open():

std::ifstream ifs;
ifs.open("bar.txt"); // ifstream: Opens file "bar.txt" for reading only.

std::ofstream ofs;
ofs.open("bar.txt"); // ofstream: Opens file "bar.txt" for writing only.

std::fstream iofs;
iofs.open("bar.txt"); // fstream: Opens file "bar.txt" for reading and writing.

You should always check if a file has been opened successfully (even when writing). Failures can include: the file
doesn't exist, file hasn't the right access rights, file is already in use, disk errors occurred, drive disconnected ...
Checking can be done as follows:

// Try to read the file 'foo.txt'.


std::ifstream ifs("fooo.txt"); // Note the typo; the file can't be opened.

// Check if the file has been opened successfully.


if (!ifs.is_open()) {
// The file hasn't been opened; take appropriate actions here.
throw CustomException(ifs, "File could not be opened");
}

When file path contains backslashes (for example, on Windows system) you should properly escape them:

// Open the file 'c:\folder\foo.txt' on Windows.


std::ifstream ifs("c:\\folder\\foo.txt"); // using escaped backslashes
Version≥C++11

or use raw literal:

// Open the file 'c:\folder\foo.txt' on Windows.


std::ifstream ifs(R"(c:\folder\foo.txt)"); // using raw literal

or use forward slashes instead:

// Open the file 'c:\folder\foo.txt' on Windows.


std::ifstream ifs("c:/folder/foo.txt");
Version≥C++11

If you want to open file with non-ASCII characters in path on Windows currently you can use non-standard wide
character path argument:

// Open the file '??????\foo.txt' on Windows.


std::ifstream ifs(LR"(??????\foo.txt)"); // using wide characters with raw literal

Complete C++ Tips & Secrets for Professionals 64


Section 8.3: Reading from a file
There are several ways to read data from a file.

If you know how the data is formatted, you can use the stream extraction operator (>>). Let's assume you have a file
named foo.txt which contains the following data:

John Doe 25 4 6 1987


Jane Doe 15 5 24 1976

Then you can use the following code to read that data from the file:

// Define variables.
std::ifstream is("foo.txt");
std::string firstname, lastname;
int age, bmonth, bday, byear;

// Extract firstname, lastname, age, bday month, bday day, and bday year in that order.
// Note: '>>' returns false if it reached EOF (end of file) or if the input data doesn't
// correspond to the type of the input variable (for example, the string "foo" can't be
// extracted into an 'int' variable).
while (is >> firstname >> lastname >> age >> bmonth >> bday >> byear)
// Process the data that has been read.

The stream extraction operator >> extracts every character and stops if it finds a character that can't be stored or if
it is a special character:

For string types, the operator stops at a whitespace ( ) or at a newline (\n).


For numbers, the operator stops at a non-number character.

This means that the following version of the file foo.txt will also be successfully read by the previous code:

John
Doe 25
4 6 1987

Jane
Doe
15 5
24
1976

The stream extraction operator >> always returns the stream given to it. Therefore, multiple operators can be
chained together in order to read data consecutively. However, a stream can also be used as a Boolean expression
(as shown in the while loop in the previous code). This is because the stream classes have a conversion operator
for the type bool. This bool() operator will return true as long as the stream has no errors. If a stream goes into an
error state (for example, because no more data can be extracted), then the bool() operator will return false.
Therefore, the while loop in the previous code will be exited after the input file has been read to its end.

If you wish to read an entire file as a string, you may use the following code:

// Opens 'foo.txt'.
std::ifstream is("foo.txt");
std::string whole_file;

// Sets position to the end of the file.


is.seekg(0, std::ios::end);

// Reserves memory for the file.

Complete C++ Tips & Secrets for Professionals 65


whole_file.reserve(is.tellg());

// Sets position to the start of the file.


is.seekg(0, std::ios::beg);

// Sets contents of 'whole_file' to all characters in the file.


whole_file.assign(std::istreambuf_iterator<char>(is),
std::istreambuf_iterator<char>());

This code reserves space for the string in order to cut down on unneeded memory allocations.

If you want to read a file line by line, you can use the function getline():

std::ifstream is("foo.txt");

// The function getline returns false if there are no more lines.


for (std::string str; std::getline(is, str);) {
// Process the line that has been read.
}

If you want to read a fixed number of characters, you can use the stream's member function read():

std::ifstream is("foo.txt");
char str[4];

// Read 4 characters from the file.


is.read(str, 4);

After executing a read command, you should always check if the error state flag failbit has been set, as it
indicates whether the operation failed or not. This can be done by calling the file stream's member function fail():

is.read(str, 4); // This operation might fail for any reason.

if (is.fail())
// Failed to read!

Section 8.4: Opening modes


When creating a file stream, you can specify an opening mode. An opening mode is basically a setting to control
how the stream opens the file.

(All modes can be found in the std::ios namespace.)

An opening mode can be provided as second parameter to the constructor of a file stream or to its open() member
function:

std::ofstream os("foo.txt", std::ios::out | std::ios::trunc);

std::ifstream is;
is.open("foo.txt", std::ios::in | std::ios::binary);

It is to be noted that you have to set ios::in or ios::out if you want to set other flags as they are not implicitly set
by the iostream members although they have a correct default value.

If you don't specify an opening mode, then the following default modes are used:

ifstream - in
ofstream - out
fstream - in and out

Complete C++ Tips & Secrets for Professionals 66


The file opening modes that you may specify by design are:

Mode Meaning For Description


app append Output Appends data at the end of the file.
binary binary Input/Output Input and output is done in binary.
in input Input Opens the file for reading.
out output Output Opens the file for writing.
trunc truncate Input/Output Removes contents of the file when opening.
ate at end Input Goes to the end of the file when opening.

Note: Setting the binary mode lets the data be read/written exactly as-is; not setting it enables the translation of
the newline '\n' character to/from a platform specific end of line sequence.

For example on Windows the end of line sequence is CRLF ("\r\n").


Write: "\n" => "\r\n"
Read: "\r\n" => "\n"

Section 8.5: Reading an ASCII file into a std::string


std::ifstream f("file.txt");

if (f)
{
std::stringstream buffer;
buffer << f.rdbuf();
f.close();

// The content of "file.txt" is available in the string `buffer.str()`


}

The rdbuf() method returns a pointer to a streambuf that can be pushed into buffer via the
stringstream::operator<< member function.

Another possibility (popularized in Effective STL by Scott Meyers) is:

std::ifstream f("file.txt");

if (f)
{
std::string str((std::istreambuf_iterator<char>(f)),
std::istreambuf_iterator<char>());

// Operations on `str`...
}

This is nice because requires little code (and allows reading a file directly into any STL container, not only strings)
but can be slow for big files.

NOTE: the extra parentheses around the first argument to the string constructor are essential to prevent the most
vexing parse problem.

Last but not least:

std::ifstream f("file.txt");

if (f)
{
f.seekg(0, std::ios::end);
const auto size = f.tellg();

Complete C++ Tips & Secrets for Professionals 67


std::string str(size, ' ');
f.seekg(0);
f.read(&str[0], size);
f.close();

// Operations on `str`...
}

which is probably the fastest option (among the three proposed).

Section 8.6: Writing files with non-standard locale settings


If you need to write a file using different locale settings to the default, you can use std::locale and
std::basic_ios::imbue() to do that for a specific file stream:

Guidance for use:

You should always apply a local to a stream before opening the file.
Once the stream has been imbued you should not change the locale.

Reasons for Restrictions: Imbuing a file stream with a locale has undefined behavior if the current locale is not
state independent or not pointing at the beginning of the file.

UTF-8 streams (and others) are not state independent. Also a file stream with a UTF-8 locale may try and read the
BOM marker from the file when it is opened; so just opening the file may read characters from the file and it will
not be at the beginning.

#include <iostream>
#include <fstream>
#include <locale>

int main()
{
std::cout << "User-preferred locale setting is "
<< std::locale("").name().c_str() << std::endl;

// Write a floating-point value using the user's preferred locale.


std::ofstream ofs1;
ofs1.imbue(std::locale(""));
ofs1.open("file1.txt");
ofs1 << 78123.456 << std::endl;

// Use a specific locale (names are system-dependent)


std::ofstream ofs2;
ofs2.imbue(std::locale("en_US.UTF-8"));
ofs2.open("file2.txt");
ofs2 << 78123.456 << std::endl;

// Switch to the classic "C" locale


std::ofstream ofs3;
ofs3.imbue(std::locale::classic());
ofs3.open("file3.txt");
ofs3 << 78123.456 << std::endl;
}

Explicitly switching to the classic "C" locale is useful if your program uses a different default locale and you want to
ensure a fixed standard for reading and writing files. With a "C" preferred locale, the example writes

78,123.456
78,123.456

Complete C++ Tips & Secrets for Professionals 68


78123.456

If, for example, the preferred locale is German and hence uses a different number format, the example writes

78 123,456
78,123.456
78123.456

(note the decimal comma in the first line).

Section 8.7: Checking end of file inside a loop condition, bad practice?
eof returns true only after reading the end of file. It does NOT indicate that the next read will be the end of
stream.

while (!f.eof())
{
// Everything is OK

f >> buffer;

// What if *only* now the eof / fail bit is set?

/* Use `buffer` */
}

You could correctly write:

while (!f.eof())
{
f >> buffer >> std::ws;

if (f.fail())
break;

/* Use `buffer` */
}

but

while (f >> buffer)


{
/* Use `buffer` */
}

is simpler and less error prone.

Further references:

std::ws: discards leading whitespace from an input stream


std::basic_ios::fail: returns true if an error has occurred on the associated stream

Section 8.8: Flushing a stream


File streams are buffered by default, as are many other types of streams. This means that writes to the stream may
not cause the underlying file to change immediately. In oder to force all buffered writes to take place immediately,
you can flush the stream. You can do this either directly by invoking the flush() method or through the std::flush
stream manipulator:

std::ofstream os("foo.txt");

Complete C++ Tips & Secrets for Professionals 69


os << "Hello World!" << std::flush;

char data[3] = "Foo";


os.write(data, 3);
os.flush();

There is a stream manipulator std::endl that combines writing a newline with flushing the stream:

// Both following lines do the same thing


os << "Hello World!\n" << std::flush;
os << "Hello world!" << std::endl;

Buffering can improve the performance of writing to a stream. Therefore, applications that do a lot of writing
should avoid flushing unnecessarily. Contrary, if I/O is done infrequently, applications should consider flushing
frequently in order to avoid data getting stuck in the stream object.

Section 8.9: Reading a file into a container


In the example below we use std::string and operator>> to read items from the file.

std::ifstream file("file3.txt");

std::vector<std::string> v;

std::string s;
while(file >> s) // keep reading until we run out
{
v.push_back(s);
}

In the above example we are simply iterating through the file reading one "item" at a time using operator>>. This
same affect can be achieved using the std::istream_iterator which is an input iterator that reads one "item" at a
time from the stream. Also most containers can be constructed using two iterators so we can simplify the above
code to:

std::ifstream file("file3.txt");

std::vector<std::string> v(std::istream_iterator<std::string>{file},
std::istream_iterator<std::string>{});

We can extend this to read any object types we like by simply specifying the object we want to read as the template
parameter to the std::istream_iterator. Thus we can simply extend the above to read lines (rather than words)
like this:

// Unfortunately there is no built in type that reads line using >>


// So here we build a simple helper class to do it. That will convert
// back to a string when used in string context.
struct Line
{
// Store data here
std::string data;
// Convert object to string
operator std::string const&() const {return data;}
// Read a line from a stream.
friend std::istream& operator>>(std::istream& stream, Line& line)
{
return std::getline(stream, line.data);
}
};

Complete C++ Tips & Secrets for Professionals 70


std::ifstream file("file3.txt");

// Read the lines of a file into a container.


std::vector<std::string> v(std::istream_iterator<Line>{file},
std::istream_iterator<Line>{});

Section 8.10: Copying a file


std::ifstream src("source_filename", std::ios::binary);
std::ofstream dst("dest_filename", std::ios::binary);
dst << src.rdbuf();
Version≥C++17

With C++17 the standard way to copy a file is including the <filesystem> header and using copy_file:

std::fileystem::copy_file("source_filename", "dest_filename");

The filesystem library was originally developed as boost.filesystem and finally merged to ISO C++ as of C++17.

Section 8.11: Closing a file


Explicitly closing a file is rarely necessary in C++, as a file stream will automatically close its associated file in its
destructor. However, you should try to limit the lifetime of a file stream object, so that it does not keep the file
handle open longer than necessary. For example, this can be done by putting all file operations into an own scope
({}):

std::string const prepared_data = prepare_data();


{
// Open a file for writing.
std::ofstream output("foo.txt");

// Write data.
output << prepared_data;
} // The ofstream will go out of scope here.
// Its destructor will take care of closing the file properly.

Calling close() explicitly is only necessary if you want to reuse the same fstream object later, but don't want to
keep the file open in between:

// Open the file "foo.txt" for the first time.


std::ofstream output("foo.txt");

// Get some data to write from somewhere.


std::string const prepared_data = prepare_data();

// Write data to the file "foo.txt".


output << prepared_data;

// Close the file "foo.txt".


output.close();

// Preparing data might take a long time. Therefore, we don't open the output file stream
// before we actually can write some data to it.
std::string const more_prepared_data = prepare_complex_data();

// Open the file "foo.txt" for the second time once we are ready for writing.
output.open("foo.txt");

// Write the data to the file "foo.txt".


output << more_prepared_data;

Complete C++ Tips & Secrets for Professionals 71


// Close the file "foo.txt" once again.
output.close();

Section 8.12: Reading a `struct` from a formatted text file.


Version≥C++11
struct info_type
{
std::string name;
int age;
float height;

// we define an overload of operator>> as a friend function which


// gives in privileged access to private data members
friend std::istream& operator>>(std::istream& is, info_type& info)
{
// skip whitespace
is >> std::ws;
std::getline(is, info.name);
is >> info.age;
is >> info.height;
return is;
}
};

void func4()
{
auto file = std::ifstream("file4.txt");

std::vector<info_type> v;

for(info_type info; file >> info;) // keep reading until we run out
{
// we only get here if the read succeeded
v.push_back(info);
}

for(auto const& info: v)


{
std::cout << " name: " << info.name << '\n';
std::cout << " age: " << info.age << " years" << '\n';
std::cout << "height: " << info.height << "lbs" << '\n';
std::cout << '\n';
}
}

file4.txt

Wogger Wabbit
2
6.2
Bilbo Baggins
111
81.3
Mary Poppins
29
154.8

Output:

name: Wogger Wabbit


age: 2 years

Complete C++ Tips & Secrets for Professionals 72


height: 6.2lbs

name: Bilbo Baggins


age: 111 years
height: 81.3lbs

name: Mary Poppins


age: 29 years
height: 154.8lbs

Chapter 9: Classes/Structures
variable.member_var = constant;
variable.member_function();
variable_pointer->member_var = constant;
variable_pointer->member_function();

Section 9.1: Class basics


A class is a user-defined type. A class is introduced with the class, struct or union keyword. In colloquial usage, the
term "class" usually refers only to non-union classes.

A class is a collection of class members, which can be:

member variables (also called "fields"),


member functions (also called "methods"),
member types or typedefs (e.g. "nested classes"),
member templates (of any kind: variable, function, class or alias template)

The class and struct keywords, called class keys, are largely interchangeable, except that the default access
specifier for members and bases is "private" for a class declared with the class key and "public" for a class declared
with the struct or union key (cf. Access modifiers).

For example, the following code snippets are identical:

struct Vector
{
int x;
int y;
int z;
};
// are equivalent to
class Vector
{
public:
int x;
int y;
int z;
};

By declaring a class` a new type is added to your program, and it is possible to instantiate objects of that class by

Vector my_vector;

Members of a class are accessed using dot-syntax.

my_vector.x = 10;
my_vector.y = my_vector.x + 1; // my_vector.y = 11;
my_vector.z = my_vector.y - 4; // my:vector.z = 7;

Complete C++ Tips & Secrets for Professionals 73


Section 9.2: Final classes and structs
Version≥C++11

Deriving a class may be forbidden with final specifier. Let's declare a final class:

class A final {
};

Now any attempt to subclass it will cause a compilation error:

// Compilation error: cannot derive from final class:


class B : public A {
};

Final class may appear anywhere in class hierarchy:

class A {
};

// OK.
class B final : public A {
};

// Compilation error: cannot derive from final class B.


class C : public B {
};

Section 9.3: Access specifiers


There are three keywords that act as access specifiers. These limit the access to class members following the
specifier, until another specifier changes the access level again:

Keyword Description
public Everyone has access
protected Only the class itself, derived classes and friends have access
private Only the class itself and friends have access

When the type is defined using the class keyword, the default access specifier is private, but if the type is defined
using the struct keyword, the default access specifier is public:

struct MyStruct { int x; };


class MyClass { int x; };

MyStruct s;
s.x = 9; // well formed, because x is public

MyClass c;
c.x = 9; // ill-formed, because x is private

Access specifiers are mostly used to limit access to internal fields and methods, and force the programmer to use a
specific interface, for example to force use of getters and setters instead of referencing a variable directly:

class MyClass {

public: /* Methods: */

int x() const noexcept { return m_x; }


void setX(int const x) noexcept { m_x = x; }

private: /* Fields: */

Complete C++ Tips & Secrets for Professionals 74


int m_x;

};

Using protected is useful for allowing certain functionality of the type to be only accessible to the derived classes,
for example, in the following code, the method calculateValue() is only accessible to classes deriving from the
base class Plus2Base, such as FortyTwo:

struct Plus2Base {
int value() noexcept { return calculateValue() + 2; }
protected: /* Methods: */
virtual int calculateValue() noexcept = 0;
};
struct FortyTwo: Plus2Base {
protected: /* Methods: */
int calculateValue() noexcept final override { return 40; }
};

Note that the friend keyword can be used to add access exceptions to functions or types for accessing protected
and private members.

The public, protected, and private keywords can also be used to grant or limit access to base class subobjects.
See the Inheritance example.

Section 9.4: Inheritance


Classes/structs can have inheritance relations.

If a class/struct B inherits from a class/struct A, this means that B has as a parent A. We say that B is a derived
class/struct from A, and A is the base class/struct.

struct A
{
public:
int p1;
protected:
int p2;
private:
int p3;
};

//Make B inherit publicly (default) from A


struct B : A
{
};

There are 3 forms of inheritance for a class/struct:

public
private
protected

Note that the default inheritance is the same as the default visibility of members: public if you use the struct
keyword, and private for the class keyword.

It's even possible to have a class derive from a struct (or vice versa). In this case, the default inheritance is
controlled by the child, so a struct that derives from a class will default to public inheritance, and a class that
derives from a struct will have private inheritance by default.

Complete C++ Tips & Secrets for Professionals 75


public inheritance:

struct B : public A // or just `struct B : A`


{
void foo()
{
p1 = 0; //well formed, p1 is public in B
p2 = 0; //well formed, p2 is protected in B
p3 = 0; //ill formed, p3 is private in A
}
};

B b;
b.p1 = 1; //well formed, p1 is public
b.p2 = 1; //ill formed, p2 is protected
b.p3 = 1; //ill formed, p3 is inaccessible

private inheritance:

struct B : private A
{
void foo()
{
p1 = 0; //well formed, p1 is private in B
p2 = 0; //well formed, p2 is private in B
p3 = 0; //ill formed, p3 is private in A
}
};

B b;
b.p1 = 1; //ill formed, p1 is private
b.p2 = 1; //ill formed, p2 is private
b.p3 = 1; //ill formed, p3 is inaccessible

protected inheritance:

struct B : protected A
{
void foo()
{
p1 = 0; //well formed, p1 is protected in B
p2 = 0; //well formed, p2 is protected in B
p3 = 0; //ill formed, p3 is private in A
}
};

B b;
b.p1 = 1; //ill formed, p1 is protected
b.p2 = 1; //ill formed, p2 is protected
b.p3 = 1; //ill formed, p3 is inaccessible

Note that although protected inheritance is allowed, the actual use of it is rare. One instance of how protected
inheritance is used in application is in partial base class specialization (usually referred to as "controlled
polymorphism").

When OOP was relatively new, (public) inheritance was frequently said to model an "IS-A" relationship. That is,
public inheritance is correct only if an instance of the derived class is also an instance of the base class.

This was later refined into the Liskov Substitution Principle: public inheritance should only be used when/if an
instance of the derived class can be substituted for an instance of the base class under any possible circumstance

Complete C++ Tips & Secrets for Professionals 76


(and still make sense).

Private inheritance is typically said to embody a completely different relationship: "is implemented in terms of"
(sometimes called a "HAS-A" relationship). For example, a Stack class could inherit privately from a Vector class.
Private inheritance bears a much greater similarity to aggregation than to public inheritance.

Protected inheritance is almost never used, and there's no general agreement on what sort of relationship it
embodies.

Section 9.5: Friendship


The friend keyword is used to give other classes and functions access to private and protected members of the
class, even through they are defined outside the class`s scope.

class Animal{
private:
double weight;
double height;
public:
friend void printWeight(Animal animal);
friend class AnimalPrinter;
// A common use for a friend function is to overload the operator<< for streaming.
friend std::ostream& operator<<(std::ostream& os, Animal animal);
};

void printWeight(Animal animal)


{
std::cout << animal.weight << "\n";
}

class AnimalPrinter
{
public:
void print(const Animal& animal)
{
// Because of the `friend class AnimalPrinter;" declaration, we are
// allowed to access private members here.
std::cout << animal.weight << ", " << animal.height << std::endl;
}
}

std::ostream& operator<<(std::ostream& os, Animal animal)


{
os << "Animal height: " << animal.height << "\n";
return os;
}

int main() {
Animal animal = {10, 5};
printWeight(animal);

AnimalPrinter aPrinter;
aPrinter.print(animal);

std::cout << animal;


}
10
10, 5
Animal height: 5

Complete C++ Tips & Secrets for Professionals 77


Section 9.6: Virtual Inheritance
When using inheritance, you can specify the virtual keyword:

struct A{};
struct B: public virtual A{};

When class B has virtual base A it means that A will reside in most derived class of inheritance tree, and thus that
most derived class is also responsible for initializing that virtual base:

struct A
{
int member;
A(int param)
{
member = param;
}
};

struct B: virtual A
{
B(): A(5){}
};

struct C: B
{
C(): /*A(88)*/ {}
};

void f()
{
C object; //error since C is not initializing it's indirect virtual base `A`
}

If we un-comment /*A(88)*/ we won't get any error since C is now initializing it's indirect virtual base A.

Also note that when we're creating variable object, most derived class is C, so C is responsible for creating(calling
constructor of) A and thus value of A::member is 88, not 5 (as it would be if we were creating object of type B).

It is useful when solving the diamond problem.:

A A A
/ \ | |
B C B C
\ / \ /
D D
virtual inheritance normal inheritance

B and C both inherit from A, and D inherits from B and C, so there are 2 instances of A in D! This results in ambiguity
when you're accessing member of A through D, as the compiler has no way of knowing from which class do you
want to access that member (the one which B inherits, or the one that is inherited byC?).

Virtual inheritance solves this problem: Since virtual base resides only in most derived object, there will be only one
instance of A in D.

struct A
{
void foo() {}
};

Complete C++ Tips & Secrets for Professionals 78


struct B : public /*virtual*/ A {};
struct C : public /*virtual*/ A {};

struct D : public B, public C


{
void bar()
{
foo(); //Error, which foo? B::foo() or C::foo()? - Ambiguous
}
};

Removing the comments resolves the ambiguity.

Section 9.7: Private inheritance: restricting base class interface


Private inheritance is useful when it is required to restrict the public interface of the class:

class A {
public:
int move();
int turn();
};

class B : private A {
public:
using A::turn;
};

B b;
b.move(); // compile error
b.turn(); // OK

This approach efficiently prevents an access to the A public methods by casting to the A pointer or reference:

B b;
A& a = static_cast<A&>(b); // compile error

In the case of public inheritance such casting will provide access to all the A public methods despite on alternative
ways to prevent this in derived B, like hiding:

class B : public A {
private:
int move();
};

or private using:

class B : public A {
private:
using A::move;
};

then for both cases it is possible:

B b;
A& a = static_cast<A&>(b); // OK for public inheritance
a.move(); // OK

Section 9.8: Accessing class members


To access member variables and member functions of an object of a class, the . operator is used:

Complete C++ Tips & Secrets for Professionals 79


struct SomeStruct {
int a;
int b;
void foo() {}
};

SomeStruct var;
// Accessing member variable a in var.
std::cout << var.a << std::endl;
// Assigning member variable b in var.
var.b = 1;
// Calling a member function.
var.foo();

When accessing the members of a class via a pointer, the -> operator is commonly used. Alternatively, the instance
can be dereferenced and the . operator used, although this is less common:

struct SomeStruct {
int a;
int b;
void foo() {}
};

SomeStruct var;
SomeStruct *p = &var;
// Accessing member variable a in var via pointer.
std::cout << p->a << std::endl;
std::cout << (*p).a << std::endl;
// Assigning member variable b in var via pointer.
p->b = 1;
(*p).b = 1;
// Calling a member function via a pointer.
p->foo();
(*p).foo();

When accessing static class members, the :: operator is used, but on the name of the class instead of an instance
of it. Alternatively, the static member can be accessed from an instance or a pointer to an instance using the . or ->
operator, respectively, with the same syntax as accessing non-static members.

struct SomeStruct {
int a;
int b;
void foo() {}

static int c;
static void bar() {}
};
int SomeStruct::c;

SomeStruct var;
SomeStruct* p = &var;
// Assigning static member variable c in struct SomeStruct.
SomeStruct::c = 5;
// Accessing static member variable c in struct SomeStruct, through var and p.
var.a = var.c;
var.b = p->c;
// Calling a static member function.
SomeStruct::bar();
var.bar();
p->bar();
Background

Complete C++ Tips & Secrets for Professionals 80


The -> operator is needed because the member access operator . has precedence over the dereferencing operator
*.

One would expect that *p.a would dereference p (resulting in a reference to the object p is pointing to) and then
accessing its member a. But in fact, it tries to access the member a of p and then dereference it. I.e. *p.a is
equivalent to *(p.a). In the example above, this would result in a compiler error because of two facts: First, p is a
pointer and does not have a member a. Second, a is an integer and, thus, can't be dereferenced.

The uncommonly used solution to this problem would be to explicitly control the precedence: (*p).a

Instead, the -> operator is almost always used. It is a short-hand for first dereferencing the pointer and then
accessing it. I.e. (*p).a is exactly the same as p->a.

The :: operator is the scope operator, used in the same manner as accessing a member of a namespace. This is
because a static class member is considered to be in that class' scope, but isn't considered a member of instances
of that class. The use of normal . and -> is also allowed for static members, despite them not being instance
members, for historical reasons; this is of use for writing generic code in templates, as the caller doesn't need to be
concerned with whether a given member function is static or non-static.

Section 9.9: Member Types and Aliases


A class or struct can also define member type aliases, which are type aliases contained within, and treated as
members of, the class itself.

struct IHaveATypedef {
typedef int MyTypedef;
};

struct IHaveATemplateTypedef {
template<typename T>
using MyTemplateTypedef = std::vector<T>;
};

Like static members, these typedefs are accessed using the scope operator, ::.

IHaveATypedef::MyTypedef i = 5; // i is an int.

IHaveATemplateTypedef::MyTemplateTypedef<int> v; // v is a std::vector<int>.

As with normal type aliases, each member type alias is allowed to refer to any type defined or aliased before, but
not after, its definition. Likewise, a typedef outside the class definition can refer to any accessible typedefs within
the class definition, provided it comes after the class definition.

template<typename T>
struct Helper {
T get() const { return static_cast<T>(42); }
};

struct IHaveTypedefs {
// typedef MyTypedef NonLinearTypedef; // Error if uncommented.
typedef int MyTypedef;
typedef Helper<MyTypedef> MyTypedefHelper;
};

IHaveTypedefs::MyTypedef i; // x_i is an int.


IHaveTypedefs::MyTypedefHelper hi; // x_hi is a Helper<int>.

typedef IHaveTypedefs::MyTypedef TypedefBeFree;


TypedefBeFree ii; // ii is an int.

Complete C++ Tips & Secrets for Professionals 81


Member type aliases can be declared with any access level, and will respect the appropriate access modifier.

class TypedefAccessLevels {
typedef int PrvInt;

protected:
typedef int ProInt;

public:
typedef int PubInt;
};

TypedefAccessLevels::PrvInt prv_i; // Error: TypedefAccessLevels::PrvInt is private.


TypedefAccessLevels::ProInt pro_i; // Error: TypedefAccessLevels::ProInt is protected.
TypedefAccessLevels::PubInt pub_i; // Good.

class Derived : public TypedefAccessLevels {


PrvInt prv_i; // Error: TypedefAccessLevels::PrvInt is private.
ProInt pro_i; // Good.
PubInt pub_i; // Good.
};

This can be used to provide a level of abstraction, allowing a class' designer to change its internal workings without
breaking code that relies on it.

class Something {
friend class SomeComplexType;

short s;
// ...

public:
typedef SomeComplexType MyHelper;

MyHelper get_helper() const { return MyHelper(8, s, 19.5, "shoe", false); }

// ...
};

// ...

Something s;
Something::MyHelper hlp = s.get_helper();

In this situation, if the helper class is changed from SomeComplexType to some other type, only the typedef and the
friend declaration would need to be modified; as long as the helper class provides the same functionality, any code
that uses it as Something::MyHelper instead of specifying it by name will usually still work without any
modifications. In this manner, we minimise the amount of code that needs to be modified when the underlying
implementation is changed, such that the type name only needs to be changed in one location.

This can also be combined with decltype, if one so desires.

class SomethingElse {
AnotherComplexType<bool, int, SomeThirdClass> helper;

public:
typedef decltype(helper) MyHelper;

private:
InternalVariable<MyHelper> ivh;

Complete C++ Tips & Secrets for Professionals 82


// ...

public:
MyHelper& get_helper() const { return helper; }

// ...
};

In this situation, changing the implementation of SomethingElse::helper will automatically change the typedef for
us, due to decltype. This minimises the number of modifications necessary when we want to change helper, which
minimises the risk of human error.

As with everything, however, this can be taken too far. If the typename is only used once or twice internally and
zero times externally, for example, there's no need to provide an alias for it. If it's used hundreds or thousands of
times throughout a project, or if it has a long enough name, then it can be useful to provide it as a typedef instead
of always using it in absolute terms. One must balance forwards compatibility and convenience with the amount of
unnecessary noise created.

This can also be used with template classes, to provide access to the template parameters from outside the class.

template<typename T>
class SomeClass {
// ...

public:
typedef T MyParam;
MyParam getParam() { return static_cast<T>(42); }
};

template<typename T>
typename T::MyParam some_func(T& t) {
return t.getParam();
}

SomeClass<int> si;
int i = some_func(si);

This is commonly used with containers, which will usually provide their element type, and other helper types, as
member type aliases. Most of the containers in the C++ standard library, for example, provide the following 12
helper types, along with any other special types they might need.

template<typename T>
class SomeContainer {
// ...

public:
// Let's provide the same helper types as most standard containers.
typedef T value_type;
typedef std::allocator<value_type> allocator_type;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef MyIterator<value_type> iterator;
typedef MyConstIterator<value_type> const_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
};

Complete C++ Tips & Secrets for Professionals 83


Prior to C++11, it was also commonly used to provide a "template typedef" of sorts, as the feature wasn't yet
available; these have become a bit less common with the introduction of alias templates, but are still useful in some
situations (and are combined with alias templates in other situations, which can be very useful for obtaining
individual components of a complex type such as a function pointer). They commonly use the name type for their
type alias.

template<typename T>
struct TemplateTypedef {
typedef T type;
}

TemplateTypedef<int>::type i; // i is an int.

This was often used with types with multiple template parameters, to provide an alias that defines one or more of
the parameters.

template<typename T, size_t SZ, size_t D>


class Array { /* ... */ };

template<typename T, size_t SZ>


struct OneDArray {
typedef Array<T, SZ, 1> type;
};

template<typename T, size_t SZ>


struct TwoDArray {
typedef Array<T, SZ, 2> type;
};

template<typename T>
struct MonoDisplayLine {
typedef Array<T, 80, 1> type;
};

OneDArray<int, 3>::type arr1i; // arr1i is an Array<int, 3, 1>.


TwoDArray<short, 5>::type arr2s; // arr2s is an Array<short, 5, 2>.
MonoDisplayLine<char>::type arr3c; // arr3c is an Array<char, 80, 1>.

Section 9.10: Nested Classes/Structures


A class or struct can also contain another class/struct definition inside itself, which is called a "nested class"; in
this situation, the containing class is referred to as the "enclosing class". The nested class definition is considered to
be a member of the enclosing class, but is otherwise separate.

struct Outer {
struct Inner { };
};

From outside of the enclosing class, nested classes are accessed using the scope operator. From inside the
enclosing class, however, nested classes can be used without qualifiers:

struct Outer {
struct Inner { };

Inner in;
};

// ...

Outer o;

Complete C++ Tips & Secrets for Professionals 84


Outer::Inner i = o.in;

As with a non-nested class/struct, member functions and static variables can be defined either within a nested
class, or in the enclosing namespace. However, they cannot be defined within the enclosing class, due to it being
considered to be a different class than the nested class.

// Bad.
struct Outer {
struct Inner {
void do_something();
};

void Inner::do_something() {}
};

// Good.
struct Outer {
struct Inner {
void do_something();
};

};

void Outer::Inner::do_something() {}

As with non-nested classes, nested classes can be forward declared and defined later, provided they are defined
before being used directly.

class Outer {
class Inner1;
class Inner2;

class Inner1 {};

Inner1 in1;
Inner2* in2p;

public:
Outer();
~Outer();
};

class Outer::Inner2 {};

Outer::Outer() : in1(Inner1()), in2p(new Inner2) {}


Outer::~Outer() {
if (in2p) { delete in2p; }
}
Version<C++11

Prior to C++11, nested classes only had access to type names, static members, and enumerators from the
enclosing class; all other members defined in the enclosing class were off-limits.

Version≥C++11

As of C++11, nested classes, and members thereof, are treated as if they were friends of the enclosing class, and
can access all of its members, according to the usual access rules; if members of the nested class require the ability
to evaluate one or more non-static members of the enclosing class, they must therefore be passed an instance:

class Outer {

Complete C++ Tips & Secrets for Professionals 85


struct Inner {
int get_sizeof_x() {
return sizeof(x); // Legal (C++11): x is unevaluated, so no instance is required.
}

int get_x() {
return x; // Illegal: Can't access non-static member without an instance.
}

int get_x(Outer& o) {
return o.x; // Legal (C++11): As a member of Outer, Inner can access private members.
}
};

int x;
};

Conversely, the enclosing class is not treated as a friend of the nested class, and thus cannot access its private
members without explicitly being granted permission.

class Outer {
class Inner {
// friend class Outer;

int x;
};

Inner in;

public:
int get_x() {
return in.x; // Error: int Outer::Inner::x is private.
// Uncomment "friend" line above to fix.
}
};

Friends of a nested class are not automatically considered friends of the enclosing class; if they need to be friends
of the enclosing class as well, this must be declared separately. Conversely, as the enclosing class is not
automatically considered a friend of the nested class, neither will friends of the enclosing class be considered
friends of the nested class.

class Outer {
friend void barge_out(Outer& out, Inner& in);

class Inner {
friend void barge_in(Outer& out, Inner& in);

int i;
};

int o;
};

void barge_in(Outer& out, Outer::Inner& in) {


int i = in.i; // Good.
int o = out.o; // Error: int Outer::o is private.
}

void barge_out(Outer& out, Outer::Inner& in) {


int i = in.i; // Error: int Outer::Inner::i is private.
int o = out.o; // Good.

Complete C++ Tips & Secrets for Professionals 86


}

As with all other class members, nested classes can only be named from outside the class if they have public
access. However, you are allowed to access them regardless of access modifier, as long as you don't explicitly name
them.

class Outer {
struct Inner {
void func() { std::cout << "I have no private taboo.\n"; }
};

public:
static Inner make_Inner() { return Inner(); }
};

// ...

Outer::Inner oi; // Error: Outer::Inner is private.

auto oi = Outer::make_Inner(); // Good.


oi.func(); // Good.
Outer::make_Inner().func(); // Good.

You can also create a type alias for a nested class. If a type alias is contained in the enclosing class, the nested type
and the type alias can have different access modifiers. If the type alias is outside the enclosing class, it requires that
either the nested class, or a typedef thereof, be public.

class Outer {
class Inner_ {};

public:
typedef Inner_ Inner;
};

typedef Outer::Inner ImOut; // Good.


typedef Outer::Inner_ ImBad; // Error.

// ...

Outer::Inner oi; // Good.


Outer::Inner_ oi; // Error.
ImOut oi; // Good.

As with other classes, nested classes can both derive from or be derived from by other classes.

struct Base {};

struct Outer {
struct Inner : Base {};
};

struct Derived : Outer::Inner {};

This can be useful in situations where the enclosing class is derived from by another class, by allowing the
programmer to update the nested class as necessary. This can be combined with a typedef to provide a consistent
name for each enclosing class' nested class:

class BaseOuter {
struct BaseInner_ {
virtual void do_something() {}
virtual void do_something_else();

Complete C++ Tips & Secrets for Professionals 87


} b_in;

public:
typedef BaseInner_ Inner;

virtual ~BaseOuter() = default;

virtual Inner& getInner() { return b_in; }


};

void BaseOuter::BaseInner_::do_something_else() {}

// ---

class DerivedOuter : public BaseOuter {


// Note the use of the qualified typedef; BaseOuter::BaseInner_ is private.
struct DerivedInner_ : BaseOuter::Inner {
void do_something() override {}
void do_something_else() override;
} d_in;

public:
typedef DerivedInner_ Inner;

BaseOuter::Inner& getInner() override { return d_in; }


};

void DerivedOuter::DerivedInner_::do_something_else() {}

// ...

// Calls BaseOuter::BaseInner_::do_something();
BaseOuter* b = new BaseOuter;
BaseOuter::Inner& bin = b->getInner();
bin.do_something();
b->getInner().do_something();

// Calls DerivedOuter::DerivedInner_::do_something();
BaseOuter* d = new DerivedOuter;
BaseOuter::Inner& din = d->getInner();
din.do_something();
d->getInner().do_something();

In the above case, both BaseOuter and DerivedOuter supply the member type Inner, as BaseInner_ and
DerivedInner_, respectively. This allows nested types to be derived without breaking the enclosing class' interface,
and allows the nested type to be used polymorphically.

Section 9.11: Unnamed struct/class


Unnamed struct is allowed (type has no name)

void foo()
{
struct /* No name */ {
float x;
float y;
} point;

point.x = 42;
}

Complete C++ Tips & Secrets for Professionals 88


or

struct Circle
{
struct /* No name */ {
float x;
float y;
} center; // but a member name
float radius;
};

and later

Circle circle;
circle.center.x = 42.f;

but NOT anonymous struct (unnamed type and unnamed object)

struct InvalidCircle
{
struct /* No name */ {
float centerX;
float centerY;
}; // No member either.
float radius;
};

Note: Some compilers allow anonymous struct as extension.

Version≥C++11

lamdba can be seen as a special unnamed struct.

decltype allows to retrieve the type of unnamed struct:

decltype(circle.point) otherPoint;

unnamed struct instance can be parameter of template method:

void print_square_coordinates()
{
const struct {float x; float y;} points[] = {
{-1, -1}, {-1, 1}, {1, -1}, {1, 1}
};

// for range relies on `template <class T, std::size_t N> std::begin(T (&)[N])`


for (const auto& point : points) {
std::cout << "{" << point.x << ", " << point.y << "}\n";
}

decltype(points[0]) topRightCorner{1, 1};


auto it = std::find(points, points + 4, topRightCorner);
std::cout << "top right corner is the "
<< 1 + std::distance(points, it) << "th\n";
}

Section 9.12: Static class members


A class is also allowed to have static members, which can be either variables or functions. These are considered to

Complete C++ Tips & Secrets for Professionals 89


be in the class' scope, but aren't treated as normal members; they have static storage duration (they exist from the
start of the program to the end), aren't tied to a particular instance of the class, and only one copy exists for the
entire class.

class Example {
static int num_instances; // Static data member (static member variable).
int i; // Non-static member variable.

public:
static std::string static_str; // Static data member (static member variable).
static int static_func(); // Static member function.

// Non-static member functions can modify static member variables.


Example() { ++num_instances; }
void set_str(const std::string& str);
};

int Example::num_instances;
std::string Example::static_str = "Hello.";

// ...

Example one, two, three;


// Each Example has its own "i", such that:
// (&one.i != &two.i)
// (&one.i != &three.i)
// (&two.i != &three.i).
// All three Examples share "num_instances", such that:
// (&one.num_instances == &two.num_instances)
// (&one.num_instances == &three.num_instances)
// (&two.num_instances == &three.num_instances)

Static member variables are not considered to be defined inside the class, only declared, and thus have their
definition outside the class definition; the programmer is allowed, but not required, to initialise static variables in
their definition. When defining the member variables, the keyword static is omitted.

class Example {
static int num_instances; // Declaration.

public:
static std::string static_str; // Declaration.

// ...
};

int Example::num_instances; // Definition. Zero-initialised.


std::string Example::static_str = "Hello."; // Definition.

Due to this, static variables can be incomplete types (apart from void), as long as they're later defined as a
complete type.

struct ForwardDeclared;

class ExIncomplete {
static ForwardDeclared fd;
static ExIncomplete i_contain_myself;
static int an_array[];
};

struct ForwardDeclared {};

Complete C++ Tips & Secrets for Professionals 90


ForwardDeclared ExIncomplete::fd;
ExIncomplete ExIncomplete::i_contain_myself;
int ExIncomplete::an_array[5];

Static member functions can be defined inside or outside the class definition, as with normal member functions. As
with static member variables, the keyword static is omitted when defining static member functions outside the
class definition.

// For Example above, either...


class Example {
// ...

public:
static int static_func() { return num_instances; }

// ...

void set_str(const std::string& str) { static_str = str; }


};

// Or...

class Example { /* ... */ };

int Example::static_func() { return num_instances; }


void Example::set_str(const std::string& str) { static_str = str; }

If a static member variable is declared const but not volatile, and is of an integral or enumeration type, it can be
initialised at declaration, inside the class definition.

enum E { VAL = 5 };

struct ExConst {
const static int ci = 5; // Good.
static const E ce = VAL; // Good.
const static double cd = 5; // Error.
static const volatile int cvi = 5; // Error.

const static double good_cd;


static const volatile int good_cvi;
};

const double ExConst::good_cd = 5; // Good.


const volatile int ExConst::good_cvi = 5; // Good.
Version≥C++11

As of C++11, static member variables of LiteralType types (types that can be constructed at compile time,
according to constexpr rules) can also be declared as constexpr; if so, they must be initialised within the class
definition.

struct ExConstexpr {
constexpr static int ci = 5; // Good.
static constexpr double cd = 5; // Good.
constexpr static int carr[] = { 1, 1, 2 }; // Good.
static constexpr ConstexprConstructibleClass c{}; // Good.
constexpr static int bad_ci; // Error.
};

constexpr int ExConstexpr::bad_ci = 5; // Still an error.

If a const or constexpr static member variable is odr-used (informally, if it has its address taken or is assigned to a

Complete C++ Tips & Secrets for Professionals 91


reference), then it must still have a separate definition, outside the class definition. This definition is not allowed to
contain an initialiser.

struct ExODR {
static const int odr_used = 5;
};

// const int ExODR::odr_used;

const int* odr_user = & ExODR::odr_used; // Error; uncomment above line to resolve.

As static members aren't tied to a given instance, they can be accessed using the scope operator, ::.

std::string str = Example::static_str;

They can also be accessed as if they were normal, non-static members. This is of historical significance, but is used
less commonly than the scope operator to prevent confusion over whether a member is static or non-static.

Example ex;
std::string rts = ex.static_str;

Class members are able to access static members without qualifying their scope, as with non-static class members.

class ExTwo {
static int num_instances;
int my_num;

public:
ExTwo() : my_num(num_instances++) {}

static int get_total_instances() { return num_instances; }


int get_instance_number() const { return my_num; }
};

int ExTwo::num_instances;

They cannot be mutable, nor would they need to be; as they aren't tied to any given instance, whether an instance
is or isn't const doesn't affect static members.

struct ExDontNeedMutable {
int immuta;
mutable int muta;

static int i;

ExDontNeedMutable() : immuta(-5), muta(-5) {}


};
int ExDontNeedMutable::i;

// ...

const ExDontNeedMutable dnm;


dnm.immuta = 5; // Error: Can't modify read-only object.
dnm.muta = 5; // Good. Mutable fields of const objects can be written.
dnm.i = 5; // Good. Static members can be written regardless of an instance's const-ness.

Static members respect access modifiers, just like non-static members.

class ExAccess {
static int prv_int;

Complete C++ Tips & Secrets for Professionals 92


protected:
static int pro_int;

public:
static int pub_int;
};

int ExAccess::prv_int;
int ExAccess::pro_int;
int ExAccess::pub_int;

// ...

int x1 = ExAccess::prv_int; // Error: int ExAccess::prv_int is private.


int x2 = ExAccess::pro_int; // Error: int ExAccess::pro_int is protected.
int x3 = ExAccess::pub_int; // Good.

As they aren't tied to a given instance, static member functions have no this pointer; due to this, they can't access
non-static member variables unless passed an instance.

class ExInstanceRequired {
int i;

public:
ExInstanceRequired() : i(0) {}

static void bad_mutate() { ++i *= 5; } // Error.


static void good_mutate(ExInstanceRequired& e) { ++e.i *= 5; } // Good.
};

Due to not having a this pointer, their addresses can't be stored in pointers-to-member-functions, and are instead
stored in normal pointers-to-functions.

struct ExPointer {
void nsfunc() {}
static void sfunc() {}
};

typedef void (ExPointer::* mem_f_ptr)();


typedef void (*f_ptr)();

mem_f_ptr p_sf = &ExPointer::sfunc; // Error.


f_ptr p_sf = &ExPointer::sfunc; // Good.

Due to not having a this pointer, they also cannot be const or volatile, nor can they have ref-qualifiers. They also
cannot be virtual.

struct ExCVQualifiersAndVirtual {
static void func() {} // Good.
static void cfunc() const {} // Error.
static void vfunc() volatile {} // Error.
static void cvfunc() const volatile {} // Error.
static void rfunc() & {} // Error.
static void rvfunc() && {} // Error.

virtual static void vsfunc() {} // Error.


static virtual void svfunc() {} // Error.
};

As they aren't tied to a given instance, static member variables are effectively treated as special global variables;
they're created when the program starts, and destroyed when it exits, regardless of whether any instances of the

Complete C++ Tips & Secrets for Professionals 93


class actually exist. Only a single copy of each static member variable exists (unless the variable is declared
thread_local (C++11 or later), in which case there's one copy per thread).

Static member variables have the same linkage as the class, whether the class has external or internal linkage. Local
classes and unnamed classes aren't allowed to have static members.

Section 9.13: Multiple Inheritance


Aside from single inheritance:

class A {};
class B : public A {};

You can also have multiple inheritance:

class A {};
class B {};
class C : public A, public B {};

C will now have inherit from A and B at the same time.

Note: this can lead to ambiguity if the same names are used in multiple inherited classs or structs. Be careful!

Ambiguity in Multiple Inheritance

Multiple inheritance may be helpful in certain cases but, sometimes odd sort of problem encounters while using
multiple inheritance.

For example: Two base classes have functions with same name which is not overridden in derived class and if you
write code to access that function using object of derived class, compiler shows error because, it cannot determine
which function to call. Here is a code for this type of ambiguity in multiple inheritance.

class base1
{
public:
void funtion( )
{ //code for base1 function }
};
class base2
{
void function( )
{ // code for base2 function }
};

class derived : public base1, public base2


{

};

int main()
{
derived obj;

// Error because compiler can't figure out which function to call


//either function( ) of base1 or base2 .
obj.function( )
}

But, this problem can be solved using scope resolution function to specify which function to class either base1 or
base2:

Complete C++ Tips & Secrets for Professionals 94


int main()
{
obj.base1::function( ); // Function of class base1 is called.
obj.base2::function( ); // Function of class base2 is called.
}

Section 9.14: Non-static member functions


A class can have non-static member functions, which operate on individual instances of the class.

class CL {
public:
void member_function() {}
};

These functions are called on an instance of the class, like so:

CL instance;
instance.member_function();

They can be defined either inside or outside the class definition; if defined outside, they are specified as being in
the class' scope.

struct ST {
void defined_inside() {}
void defined_outside();
};
void ST::defined_outside() {}

They can be CV-qualified and/or ref-qualified, affecting how they see the instance they're called upon; the function
will see the instance as having the specified cv-qualifier(s), if any. Which version is called will be based on the
instance's cv-qualifiers. If there is no version with the same cv-qualifiers as the instance, then a more-cv-qualified
version will be called if available.

struct CVQualifiers {
void func() {} // 1: Instance is non-cv-qualified.
void func() const {} // 2: Instance is const.

void cv_only() const volatile {}


};

CVQualifiers non_cv_instance;
const CVQualifiers c_instance;

non_cv_instance.func(); // Calls #1.


c_instance.func(); // Calls #2.

non_cv_instance.cv_only(); // Calls const volatile version.


c_instance.cv_only(); // Calls const volatile version.
Version≥C++11

Member function ref-qualifiers indicate whether or not the function is intended to be called on rvalue instances,
and use the same syntax as function cv-qualifiers.

struct RefQualifiers {
void func() & {} // 1: Called on normal instances.
void func() && {} // 2: Called on rvalue (temporary) instances.
};

RefQualifiers rf;
rf.func(); // Calls #1.

Complete C++ Tips & Secrets for Professionals 95


RefQualifiers{}.func(); // Calls #2.

CV-qualifiers and ref-qualifiers can also be combined if necessary.

struct BothCVAndRef {
void func() const& {} // Called on normal instances. Sees instance as const.
void func() && {} // Called on temporary instances.
};

They can also be virtual; this is fundamental to polymorphism, and allows a child class(es) to provide the same
interface as the parent class, while supplying their own functionality.

struct Base {
virtual void func() {}
};
struct Derived {
virtual void func() {}
};

Base* bp = new Base;


Base* dp = new Derived;
bp.func(); // Calls Base::func().
dp.func(); // Calls Derived::func().

For more information, see here.

Chapter 10: Smart Pointers


std::shared_ptr<ClassType> variableName = std::make_shared<ClassType>(arg1, arg2, ...);
std::shared_ptr<ClassType> variableName (new ClassType(arg1, arg2, ...));
std::unique_ptr<ClassType> variableName = std::make_unique<ClassType>(arg1, arg2, ...); //
C++14
std::unique_ptr<ClassType> variableName (new ClassType(arg1, arg2, ...));

Section 10.1: Unique ownership (std::unique_ptr)


Version≥C++11

A std::unique_ptr is a class template that manages the lifetime of a dynamically stored object. Unlike for
std::shared_ptr, the dynamic object is owned by only one instance of a std::unique_ptr at any time,

// Creates a dynamic int with value of 20 owned by a unique pointer


std::unique_ptr<int> ptr = std::make_unique<int>(20);

(Note: std::unique_ptr is available since C++11 and std::make_unique since C++14.)

Only the variable ptr holds a pointer to a dynamically allocated int. When a unique pointer that owns an object
goes out of scope, the owned object is deleted, i.e. its destructor is called if the object is of class type, and the
memory for that object is released.

To use std::unique_ptr and std::make_unique with array-types, use their array specializations:

// Creates a unique_ptr to an int with value 59


std::unique_ptr<int> ptr = std::make_unique<int>(59);

// Creates a unique_ptr to an array of 15 ints


std::unique_ptr<int[]> ptr = std::make_unique<int[]>(15);

You can access the std::unique_ptr just like a raw pointer, because it overloads those operators.

Complete C++ Tips & Secrets for Professionals 96


You can transfer ownership of the contents of a smart pointer to another pointer by using std::move, which will
cause the original smart pointer to point to nullptr.

// 1. std::unique_ptr
std::unique_ptr<int> ptr = std::make_unique<int>();

// Change value to 1
*ptr = 1;

// 2. std::unique_ptr (by moving 'ptr' to 'ptr2', 'ptr' doesn't own the object anymore)
std::unique_ptr<int> ptr2 = std::move(ptr);

int a = *ptr2; // 'a' is 1


int b = *ptr; // undefined behavior! 'ptr' is 'nullptr'
// (because of the move command above)

Passing unique_ptr to functions as parameter:

void foo(std::unique_ptr<int> ptr)


{
// Your code goes here
}

std::unique_ptr<int> ptr = std::make_unique<int>(59);


foo(std::move(ptr))

Returning unique_ptr from functions. This is the preferred C++11 way of writing factory functions, as it clearly
conveys the ownership semantics of the return: the caller owns the resulting unique_ptr and is responsible for it.

std::unique_ptr<int> foo()
{
std::unique_ptr<int> ptr = std::make_unique<int>(59);
return ptr;
}

std::unique_ptr<int> ptr = foo();

Compare this to:

int* foo_cpp03();

int* p = foo_cpp03(); // do I own p? do I have to delete it at some point?


// it's not readily apparent what the answer is.
Version<C++14

The class template make_unique is provided since C++14. It's easy to add it manually to C++11 code:

template<typename T, typename... Args>


typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(Args&&... args)
{ return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); }

// Use make_unique for arrays


template<typename T>
typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(size_t n)
{ return std::unique_ptr<T>(new typename std::remove_extent<T>::type[n]()); }
Version≥C++11

Unlike the dumb smart pointer (std::auto_ptr), unique_ptr can also be instantiated with vector allocation (not
std::vector). Earlier examples were for scalar allocations. For example to have a dynamically allocated integer

Complete C++ Tips & Secrets for Professionals 97


array for 10 elements, you would specify int[] as the template type (and not just int):

std::unique_ptr<int[]> arr_ptr = std::make_unique<int[]>(10);

Which can be simplified with:

auto arr_ptr = std::make_unique<int[]>(10);

Now, you use arr_ptr as if it is an array:

arr_ptr[2] = 10; // Modify third element

You need not to worry about de-allocation. This template specialized version calls constructors and destructors
appropriately. Using vectored version of unique_ptr or a vector itself - is a personal choice.

In versions prior to C++11, std::auto_ptr was available. Unlike unique_ptr it is allowed to copy auto_ptrs, upon
which the source ptr will lose the ownership of the contained pointer and the target receives it.

Section 10.2: Sharing ownership (std::shared_ptr)


The class template std::shared_ptr defines a shared pointer that is able to share ownership of an object with
other shared pointers. This contrasts to std::unique_ptr which represents exclusive ownership.

The sharing behavior is implemented through a technique known as reference counting, where the number of
shared pointers that point to the object is stored alongside it. When this count reaches zero, either through the
destruction or reassignment of the last std::shared_ptr instance, the object is automatically destroyed.

// Creation: 'firstShared' is a shared pointer for a new instance of 'Foo'


std::shared_ptr<Foo> firstShared = std::make_shared<Foo>(/*args*/);

To create multiple smart pointers that share the same object, we need to create another shared_ptr that aliases
the first shared pointer. Here are 2 ways of doing it:

std::shared_ptr<Foo> secondShared(firstShared); // 1st way: Copy constructing


std::shared_ptr<Foo> secondShared;
secondShared = firstShared; // 2nd way: Assigning

Either of the above ways makes secondShared a shared pointer that shares ownership of our instance of Foo with
firstShared.

The smart pointer works just like a raw pointer. This means, you can use * to dereference them. The regular ->
operator works as well:

secondShared->test(); // Calls Foo::test()

Finally, when the last aliased shared_ptr goes out of scope, the destructor of our Foo instance is called.

Warning: Constructing a shared_ptr might throw a bad_alloc exception when extra data for shared ownership
semantics needs to be allocated. If the constructor is passed a regular pointer it assumes to own the object pointed
to and calls the deleter if an exception is thrown. This means shared_ptr<T>(new T(args)) will not leak a T object if
allocation of shared_ptr<T> fails. However, it is advisable to use make_shared<T>(args) or
allocate_shared<T>(alloc, args), which enable the implementation to optimize the memory allocation.

Allocating Arrays([]) using shared_ptr

Version≥C++11 Version<C++17

Complete C++ Tips & Secrets for Professionals 98


Unfortunately, there is no direct way to allocate Arrays using make_shared<>.

It is possible to create arrays for shared_ptr<> using new and std::default_delete.

For example, to allocate an array of 10 integers, we can write the code as

shared_ptr<int> sh(new int[10], std::default_delete<int[]>());

Specifying std::default_delete is mandatory here to make sure that the allocated memory is correctly cleaned up
using delete[].

If we know the size at compile time, we can do it this way:

template<class Arr>
struct shared_array_maker {};
template<class T, std::size_t N>
struct shared_array_maker<T[N]> {
std::shared_ptr<T> operator()const{
auto r = std::make_shared<std::array<T,N>>();
if (!r) return {};
return {r.data(), r};
}
};
template<class Arr>
auto make_shared_array()
-> decltype( shared_array_maker<Arr>{}() )
{ return shared_array_maker<Arr>{}(); }

then make_shared_array<int[10]> returns a shared_ptr<int> pointing to 10 ints all default constructed.

Version≥C++17

With C++17, shared_ptr gained special support for array types. It is no longer necessary to specify the array-deleter
explicitly, and the shared pointer can be dereferenced using the [] array index operator:

std::shared_ptr<int[]> sh(new int[10]);


sh[0] = 42;

Shared pointers can point to a sub-object of the object it owns:

struct Foo { int x; };


std::shared_ptr<Foo> p1 = std::make_shared<Foo>();
std::shared_ptr<int> p2(p1, &p1->x);

Both p2 and p1 own the object of type Foo, but p2 points to its int member x. This means that if p1 goes out of
scope or is reassigned, the underlying Foo object will still be alive, ensuring that p2 does not dangle.

Important: A shared_ptr only knows about itself and all other shared_ptr that were created with the alias
constructor. It does not know about any other pointers, including all other shared_ptrs created with a reference to
the same Foo instance:

Foo *foo = new Foo;


std::shared_ptr<Foo> shared1(foo);
std::shared_ptr<Foo> shared2(foo); // don't do this

shared1.reset(); // this will delete foo, since shared1


// was the only shared_ptr that owned it

shared2->test(); // UNDEFINED BEHAVIOR: shared2's foo has been


// deleted already!!

Complete C++ Tips & Secrets for Professionals 99


Ownership Transfer of shared_ptr

By default, shared_ptr increments the reference count and doesn't transfer the ownership. However, it can be
made to transfer the ownership using std::move:

shared_ptr<int> up = make_shared<int>();
// Transferring the ownership
shared_ptr<int> up2 = move(up);
// At this point, the reference count of up = 0 and the
// ownership of the pointer is solely with up2 with reference count = 1

Section 10.3: Sharing with temporary ownership (std::weak_ptr)


Instances of std::weak_ptr can point to objects owned by instances of std::shared_ptr while only becoming
temporary owners themselves. This means that weak pointers do not alter the object's reference count and
therefore do not prevent an object's deletion if all of the object's shared pointers are reassigned or destroyed.

In the following example instances of std::weak_ptr are used so that the destruction of a tree object is not
inhibited:

#include <memory>
#include <vector>

struct TreeNode {
std::weak_ptr<TreeNode> parent;
std::vector< std::shared_ptr<TreeNode> > children;
};

int main() {
// Create a TreeNode to serve as the root/parent.
std::shared_ptr<TreeNode> root(new TreeNode);

// Give the parent 100 child nodes.


for (size_t i = 0; i < 100; ++i) {
std::shared_ptr<TreeNode> child(new TreeNode);
root->children.push_back(child);
child->parent = root;
}

// Reset the root shared pointer, destroying the root object, and
// subsequently its child nodes.
root.reset();
}

As child nodes are added to the root node's children, their std::weak_ptr member parent is set to the root node.
The member parent is declared as a weak pointer as opposed to a shared pointer such that the root node's
reference count is not incremented. When the root node is reset at the end of main(), the root is destroyed. Since
the only remaining std::shared_ptr references to the child nodes were contained in the root's collection children,
all child nodes are subsequently destroyed as well.

Due to control block implementation details, shared_ptr allocated memory may not be released until shared_ptr
reference counter and weak_ptr reference counter both reach zero.

#include <memory>
int main()
{
{
std::weak_ptr<int> wk;
{

Complete C++ Tips & Secrets for Professionals 100


// std::make_shared is optimized by allocating only once
// while std::shared_ptr<int>(new int(42)) allocates twice.
// Drawback of std::make_shared is that control block is tied to our integer
std::shared_ptr<int> sh = std::make_shared<int>(42);
wk = sh;
// sh memory should be released at this point...
}
// ... but wk is still alive and needs access to control block
}
// now memory is released (sh and wk)
}

Since std::weak_ptr does not keep its referenced object alive, direct data access through a std::weak_ptr is not
possible. Instead it provides a lock() member function that attempts to retrieve a std::shared_ptr to the
referenced object:

#include <cassert>
#include <memory>
int main()
{
{
std::weak_ptr<int> wk;
std::shared_ptr<int> sp;
{
std::shared_ptr<int> sh = std::make_shared<int>(42);
wk = sh;
// calling lock will create a shared_ptr to the object referenced by wk
sp = wk.lock();
// sh will be destroyed after this point, but sp is still alive
}
// sp still keeps the data alive.
// At this point we could even call lock() again
// to retrieve another shared_ptr to the same data from wk
assert(*sp == 42);
assert(!wk.expired());
// resetting sp will delete the data,
// as it is currently the last shared_ptr with ownership
sp.reset();
// attempting to lock wk now will return an empty shared_ptr,
// as the data has already been deleted
sp = wk.lock();
assert(!sp);
assert(wk.expired());
}
}

Section 10.4: Using custom deleters to create a wrapper to a C interface


Many C interfaces such as SDL2 have their own deletion functions. This means that you cannot use smart pointers
directly:

std::unique_ptr<SDL_Surface> a; // won't work, UNSAFE!

Instead, you need to define your own deleter. The examples here use the SDL_Surface structure which should be
freed using the SDL_FreeSurface() function, but they should be adaptable to many other C interfaces.

The deleter must be callable with a pointer argument, and therefore can be e.g. a simple function pointer:

std::unique_ptr<SDL_Surface, void(*)(SDL_Surface*)> a(pointer, SDL_FreeSurface);

Any other callable object will work, too, for example a class with an operator():

Complete C++ Tips & Secrets for Professionals 101


struct SurfaceDeleter {
void operator()(SDL_Surface* surf) {
SDL_FreeSurface(surf);
}
};

std::unique_ptr<SDL_Surface, SurfaceDeleter> a(pointer, SurfaceDeleter{}); // safe


std::unique_ptr<SDL_Surface, SurfaceDeleter> b(pointer); // equivalent to the above
// as the deleter is value-initialized

This not only provides you with safe, zero overhead (if you use unique_ptr) automatic memory management, you
also get exception safety.

Note that the deleter is part of the type for unique_ptr, and the implementation can use the empty base
optimization to avoid any change in size for empty custom deleters. So while std::unique_ptr<SDL_Surface,
SurfaceDeleter> and std::unique_ptr<SDL_Surface, void(*)(SDL_Surface*)> solve the same problem in a
similar way, the former type is still only the size of a pointer while the latter type has to hold two pointers: both the
SDL_Surface* and the function pointer! When having free function custom deleters, it is preferable to wrap the
function in an empty type.

In cases where reference counting is important, one could use a shared_ptr instead of an unique_ptr. The
shared_ptr always stores a deleter, this erases the type of the deleter, which might be useful in APIs. The
disadvantages of using shared_ptr over unique_ptr include a higher memory cost for storing the deleter and a
performance cost for maintaining the reference count.

// deleter required at construction time and is part of the type


std::unique_ptr<SDL_Surface, void(*)(SDL_Surface*)> a(pointer, SDL_FreeSurface);

// deleter is only required at construction time, not part of the type


std::shared_ptr<SDL_Surface> b(pointer, SDL_FreeSurface);
Version≥C++17

With template auto, we can make it even easier to wrap our custom deleters:

template <auto DeleteFn>


struct FunctionDeleter {
template <class T>
void operator()(T* ptr) {
DeleteFn(ptr);
}
};

template <class T, auto DeleteFn>


using unique_ptr_deleter = std::unique_ptr<T, FunctionDeleter<DeleteFn>>;

With which the above example is simply:

unique_ptr_deleter<SDL_Surface, SDL_FreeSurface> c(pointer);

Here, the purpose of auto is to handle all free functions, whether they return void (e.g. SDL_FreeSurface) or not
(e.g. fclose).

Section 10.5: Unique ownership without move semantics (auto_ptr)


Version<C++11

NOTE: std::auto_ptr has been deprecated in C++11 and will be removed in C++17. You should only use this if you
are forced to use C++03 or earlier and are willing to be careful. It is recommended to move to unique_ptr in

Complete C++ Tips & Secrets for Professionals 102


combination with std::move to replace std::auto_ptr behavior.

Before we had std::unique_ptr, before we had move semantics, we had std::auto_ptr. std::auto_ptr provides
unique ownership but transfers ownership upon copy.

As with all smart pointers, std::auto_ptr automatically cleans up resources (see RAII):

{
std::auto_ptr<int> p(new int(42));
std::cout << *p;
} // p is deleted here, no memory leaked

but allows only one owner:

std::auto_ptr<X> px = ...;
std::auto_ptr<X> py = px;
// px is now empty

This allows to use std::auto_ptr to keep ownership explicit and unique at the danger of losing ownership
unintended:

void f(std::auto_ptr<X> ) {
// assumes ownership of X
// deletes it at end of scope
};

std::auto_ptr<X> px = ...;
f(px); // f acquires ownership of underlying X
// px is now empty
px->foo(); // NPE!
// px.~auto_ptr() does NOT delete

The transfer of ownership happened in the "copy" constructor. auto_ptr's copy constructor and copy assignment
operator take their operands by non-const reference so that they could be modified. An example implementation
might be:

template <typename T>


class auto_ptr {
T* ptr;
public:
auto_ptr(auto_ptr& rhs)
: ptr(rhs.release())
{ }

auto_ptr& operator=(auto_ptr& rhs) {


reset(rhs.release());
return *this;
}

T* release() {
T* tmp = ptr;
ptr = nullptr;
return tmp;
}

void reset(T* tmp = nullptr) {


if (ptr != tmp) {
delete ptr;
ptr = tmp;
}
}

Complete C++ Tips & Secrets for Professionals 103


/* other functions ... */
};

This breaks copy semantics, which require that copying an object leaves you with two equivalent versions of it. For
any copyable type, T, I should be able to write:

T a = ...;
T b(a);
assert(b == a);

But for auto_ptr, this is not the case. As a result, it is not safe to put auto_ptrs in containers.

Section 10.6: Casting std::shared_ptr pointers


It is not possible to directly use static_cast, const_cast, dynamic_cast and reinterpret_cast on
std::shared_ptr to retrieve a pointer sharing ownership with the pointer being passed as argument. Instead, the
functions std::static_pointer_cast, std::const_pointer_cast, std::dynamic_pointer_cast and
std::reinterpret_pointer_cast should be used:

struct Base { virtual ~Base() noexcept {}; };


struct Derived: Base {};
auto derivedPtr(std::make_shared<Derived>());
auto basePtr(std::static_pointer_cast<Base>(derivedPtr));
auto constBasePtr(std::const_pointer_cast<Base const>(basePtr));
auto constDerivedPtr(std::dynamic_pointer_cast<Derived const>(constBasePtr));

Note that std::reinterpret_pointer_cast is not available in C++11 and C++14, as it was only proposed by N3920
and adopted into Library Fundamentals TS in February 2014. However, it can be implemented as follows:

template <typename To, typename From>


inline std::shared_ptr<To> reinterpret_pointer_cast(
std::shared_ptr<From> const & ptr) noexcept
{ return std::shared_ptr<To>(ptr, reinterpret_cast<To *>(ptr.get())); }

Section 10.7: Writing a smart pointer: value_ptr


A value_ptr is a smart pointer that behaves like a value. When copied, it copies its contents. When created, it
creates its contents.

// Like std::default_delete:
template<class T>
struct default_copier {
// a copier must handle a null T const* in and return null:
T* operator()(T const* tin)const {
if (!tin) return nullptr;
return new T(*tin);
}
void operator()(void* dest, T const* tin)const {
if (!tin) return;
return new(dest) T(*tin);
}
};
// tag class to handle empty case:
struct empty_ptr_t {};
constexpr empty_ptr_t empty_ptr{};
// the value pointer type itself:
template<class T, class Copier=default_copier<T>, class Deleter=std::default_delete<T>,
class Base=std::unique_ptr<T, Deleter>
>
struct value_ptr:Base, private Copier {

Complete C++ Tips & Secrets for Professionals 104


using copier_type=Copier;
// also typedefs from unique_ptr

using Base::Base;

value_ptr( T const& t ):
Base( std::make_unique<T>(t) ),
Copier()
{}
value_ptr( T && t ):
Base( std::make_unique<T>(std::move(t)) ),
Copier()
{}
// almost-never-empty:
value_ptr():
Base( std::make_unique<T>() ),
Copier()
{}
value_ptr( empty_ptr_t ) {}

value_ptr( Base b, Copier c={} ):


Base(std::move(b)),
Copier(std::move(c))
{}

Copier const& get_copier() const {


return *this;
}

value_ptr clone() const {


return {
Base(
get_copier()(this->get()),
this->get_deleter()
),
get_copier()
};
}
value_ptr(value_ptr&&)=default;
value_ptr& operator=(value_ptr&&)=default;

value_ptr(value_ptr const& o):value_ptr(o.clone()) {}


value_ptr& operator=(value_ptr const&o) {
if (o && *this) {
// if we are both non-null, assign contents:
**this = *o;
} else {
// otherwise, assign a clone (which could itself be null):
*this = o.clone();
}
return *this;
}
value_ptr& operator=( T const& t ) {
if (*this) {
**this = t;
} else {
*this = value_ptr(t);
}
return *this;
}
value_ptr& operator=( T && t ) {
if (*this) {

Complete C++ Tips & Secrets for Professionals 105


**this = std::move(t);
} else {
*this = value_ptr(std::move(t));
}
return *this;
}
T& get() { return **this; }
T const& get() const { return **this; }
T* get_pointer() {
if (!*this) return nullptr;
return std::addressof(get());
}
T const* get_pointer() const {
if (!*this) return nullptr;
return std::addressof(get());
}
// operator-> from unique_ptr
};
template<class T, class...Args>
value_ptr<T> make_value_ptr( Args&&... args ) {
return {std::make_unique<T>(std::forward<Args>(args)...)};
}

This particular value_ptr is only empty if you construct it with empty_ptr_t or if you move from it. It exposes the fact
it is a unique_ptr, so explicit operator bool() const works on it. .get() has been changed to return a
reference (as it is almost never empty), and .get_pointer() returns a pointer instead.

This smart pointer can be useful for pImpl cases, where we want value-semantics but we also don't want to expose
the contents of the pImpl outside of the implementation file.

With a non-default Copier, it can even handle virtual base classes that know how to produce instances of their
derived and turn them into value-types.

Section 10.8: Getting a shared_ptr referring to this


enable_shared_from_this enables you to get a valid shared_ptr instance to this.

By deriving your class from the class template enable_shared_from_this, you inherit a method shared_from_this
that returns a shared_ptr instance to this.

Note that the object must be created as a shared_ptr in first place:

#include <memory>
class A: public enable_shared_from_this<A> {
};
A* ap1 =new A();
shared_ptr<A> ap2(ap1); // First prepare a shared pointer to the object and hold it!
// Then get a shared pointer to the object from the object itself
shared_ptr<A> ap3 = ap1->shared_from_this();
int c3 =ap3.use_count(); // =2: pointing to the same object

Note(2) you cannot call enable_shared_from_this inside the constructor.

#include <memory> // enable_shared_from_this

class Widget : public std::enable_shared_from_this< Widget >


{
public:
void DoSomething()
{

Complete C++ Tips & Secrets for Professionals 106


std::shared_ptr< Widget > self = shared_from_this();
someEvent -> Register( self );
}
private:
...
};

int main()
{
...
auto w = std::make_shared< Widget >();
w -> DoSomething();
...
}

If you use shared_from_this() on an object not owned by a shared_ptr, such as a local automatic object or a
global object, then the behavior is undefined. Since C++17 it throws std::bad_alloc instead.

Using shared_from_this() from a constructor is equivalent to using it on an object not owned by a shared_ptr,
because the objects is possessed by the shared_ptr after the constructor returns.

Chapter 11: Function Overloading


See also separate topic on Overload Resolution

Section 11.1: What is Function Overloading?


Function overloading is having multiple functions declared in the same scope with the exact same name exist in the
same place (known as scope) differing only in their signature, meaning the arguments they accept.

Suppose you are writing a series of functions for generalized printing capabilities, beginning with std::string:

void print(const std::string &str)


{
std::cout << "This is a string: " << str << std::endl;
}

This works fine, but suppose you want a function that also accepts an int and prints that too. You could write:

void print_int(int num)


{
std::cout << "This is an int: " << num << std::endl;
}

But because the two functions accept different parameters, you can simply write:

void print(int num)


{
std::cout << "This is an int: " << num << std::endl;
}

Now you have 2 functions, both named print, but with different signatures. One accepts std::string, the other
one an int. Now you can call them without worrying about different names:

print("Hello world!"); //prints "This is a string: Hello world!"


print(1337); //prints "This is an int: 1337"

Instead of:

print("Hello world!");

Complete C++ Tips & Secrets for Professionals 107


print_int(1337);

When you have overloaded functions, the compiler infers which of the functions to call from the parameters you
provide it. Care must be taken when writing function overloads. For example, with implicit type conversions:

void print(int num)


{
std::cout << "This is an int: " << num << std::endl;
}
void print(double num)
{
std::cout << "This is a double: " << num << std::endl;
}

Now it's not immediately clear which overload of print is called when you write:

print(5);

And you might need to give your compiler some clues, like:

print(static_cast<double>(5));
print(static_cast<int>(5));
print(5.0);

Some care also needs to be taken when writing overloads that accept optional parameters:

// WRONG CODE
void print(int num1, int num2 = 0) //num2 defaults to 0 if not included
{
std::cout << "These are ints: << num1 << " and " << num2 << std::endl;
}
void print(int num)
{
std::cout << "This is an int: " << num << std::endl;
}

Because there's no way for the compiler to tell if a call like print(17) is meant for the first or second function
because of the optional second parameter, this will fail to compile.

Section 11.2: Return Type in Function Overloading


Note that you cannot overload a function based on its return type. For example:

// WRONG CODE
std::string getValue()
{
return "hello";
}

int getValue()
{
return 0;
}

int x = getValue();

This will cause a compilation error as the compiler will not be able to work out which version of getValue to call,
even though the return type is assigned to an int.

Complete C++ Tips & Secrets for Professionals 108


Section 11.3: Member Function cv-qualifier Overloading
Functions within a class can be overloaded for when they are accessed through a cv-qualified reference to that
class; this is most commonly used to overload for const, but can be used to overload for volatile and const
volatile, too. This is because all non-static member functions take this as a hidden parameter, which the cv-
qualifiers are applied to. This is most commonly used to overload for const, but can also be used for volatile and
const volatile.

This is necessary because a member function can only be called if it is at least as cv-qualified as the instance it's
called on. While a non-const instance can call both const and non-const members, a const instance can only call
const members. This allows a function to have different behaviour depending on the calling instance's cv-qualifiers,
and allows the programmer to disallow functions for an undesired cv-qualifier(s) by not providing a version with
that qualifier(s).

A class with some basic print method could be const overloaded like so:

#include <iostream>

class Integer
{
public:
Integer(int i_): i{i_}{}

void print()
{
std::cout << "int: " << i << std::endl;
}

void print() const


{
std::cout << "const int: " << i << std::endl;
}

protected:
int i;
};

int main()
{
Integer i{5};
const Integer &ic = i;

i.print(); // prints "int: 5"


ic.print(); // prints "const int: 5"
}

This is a key tenet of const correctness: By marking member functions as const, they are allowed to be called on
const instances, which in turn allows functions to take instances as const pointers/references if they don't need to
modify them. This allows code to specify whether it modifies state by taking unmodified parameters as const and
modified parameters without cv-qualifiers, making code both safer and more readable.

class ConstCorrect
{
public:
void good_func() const
{
std::cout << "I care not whether the instance is const." << std::endl;
}

Complete C++ Tips & Secrets for Professionals 109


void bad_func()
{
std::cout << "I can only be called on non-const, non-volatile instances." << std::endl;
}
};

void i_change_no_state(const ConstCorrect& cc)


{
std::cout << "I can take either a const or a non-const ConstCorrect." << std::endl;
cc.good_func(); // Good. Can be called from const or non-const instance.
cc.bad_func(); // Error. Can only be called from non-const instance.
}

void const_incorrect_func(ConstCorrect& cc)


{
cc.good_func(); // Good. Can be called from const or non-const instance.
cc.bad_func(); // Good. Can only be called from non-const instance.
}

A common usage of this is declaring accessors as const, and mutators as non-const.

No class members can be modified within a const member function. If there is some member that you really need
to modify, such as locking a std::mutex, you can declare it as mutable:

class Integer
{
public:
Integer(int i_): i{i_}{}

int get() const


{
std::lock_guard<std::mutex> lock{mut};
return i;
}

void set(int i_)


{
std::lock_guard<std::mutex> lock{mut};
i = i_;
}

protected:
int i;
mutable std::mutex mut;
};

Chapter 12: std::vector


A vector is a dynamic array with automatically handled storage. The elements in a vector can be accessed just as
efficiently as those in an array with the advantage being that vectors can dynamically change in size.

In terms of storage the vector data is (usually) placed in dynamically allocated memory thus requiring some minor
overhead; conversely C-arrays and std::array use automatic storage relative to the declared location and thus do
not have any overhead.

Section 12.1: Accessing Elements


There are two primary ways of accessing elements in a std::vector

index-based access

Complete C++ Tips & Secrets for Professionals 110


iterators

Index-based access:

This can be done either with the subscript operator [], or the member function at().

Both return a reference to the element at the respective position in the std::vector (unless it's a vector<bool>), so
that it can be read as well as modified (if the vector is not const).

[] and at() differ in that [] is not guaranteed to perform any bounds checking, while at() does. Accessing
elements where index < 0 or index >= size is undefined behavior for [], while at() throws a std::out_of_range
exception.

Note: The examples below use C++11-style initialization for clarity, but the operators can be used with all versions
(unless marked C++11).

Version≥C++11
std::vector<int> v{ 1, 2, 3 };
// using []
int a = v[1]; // a is 2
v[1] = 4; // v now contains { 1, 4, 3 }

// using at()
int b = v.at(2); // b is 3
v.at(2) = 5; // v now contains { 1, 4, 5 }
int c = v.at(3); // throws std::out_of_range exception

Because the at() method performs bounds checking and can throw exceptions, it is slower than []. This makes []
preferred code where the semantics of the operation guarantee that the index is in bounds. In any case, accesses
to elements of vectors are done in constant time. That means accessing to the first element of the vector has the
same cost (in time) of accessing the second element, the third element and so on.

For example, consider this loop

for (std::size_t i = 0; i < v.size(); ++i) {


v[i] = 1;
}

Here we know that the index variable i is always in bounds, so it would be a waste of CPU cycles to check that i is
in bounds for every call to operator[].

The front() and back() member functions allow easy reference access to the first and last element of the vector,
respectively. These positions are frequently used, and the special accessors can be more readable than their
alternatives using []:

std::vector<int> v{ 4, 5, 6 }; // In pre-C++11 this is more verbose

int a = v.front(); // a is 4, v.front() is equivalent to v[0]


v.front() = 3; // v now contains {3, 5, 6}
int b = v.back(); // b is 6, v.back() is equivalent to v[v.size() - 1]
v.back() = 7; // v now contains {3, 5, 7}

Note: It is undefined behavior to invoke front() or back() on an empty vector. You need to check that the
container is not empty using the empty() member function (which checks if the container is empty) before calling
front() or back(). A simple example of the use of 'empty()' to test for an empty vector follows:

int main ()
{
std::vector<int> v;

Complete C++ Tips & Secrets for Professionals 111


int sum (0);

for (int i=1;i<=10;i++) v.push_back(i);//create and initialize the vector

while (!v.empty())//loop through until the vector tests to be empty


{
sum += v.back();//keep a running total
v.pop_back();//pop out the element which removes it from the vector
}

std::cout << "total: " << sum << '\n';//output the total to the user

return 0;
}

The example above creates a vector with a sequence of numbers from 1 to 10. Then it pops the elements of the
vector out until the vector is empty (using 'empty()') to prevent undefined behavior. Then the sum of the numbers
in the vector is calculated and displayed to the user.

Version≥C++11

The data() method returns a pointer to the raw memory used by the std::vector to internally store its elements.
This is most often used when passing the vector data to legacy code that expects a C-style array.

std::vector<int> v{ 1, 2, 3, 4 }; // v contains {1, 2, 3, 4}


int* p = v.data(); // p points to 1
*p = 4; // v now contains {4, 2, 3, 4}
++p; // p points to 2
*p = 3; // v now contains {4, 3, 3, 4}
p[1] = 2; // v now contains {4, 3, 2, 4}
*(p + 2) = 1; // v now contains {4, 3, 2, 1}
Version<C++11

Before C++11, the data() method can be simulated by calling front() and taking the address of the returned
value:

std::vector<int> v(4);
int* ptr = &(v.front()); // or &v[0]

This works because vectors are always guaranteed to store their elements in contiguous memory locations,
assuming the contents of the vector doesn't override unary operator&. If it does, you'll have to re-implement
std::addressof in pre-C++11. It also assumes that the vector isn't empty.

Iterators:

Iterators are explained in more detail in the example "Iterating over std::vector" and the article Iterators. In short,
they act similarly to pointers to the elements of the vector:

Version≥C++11
std::vector<int> v{ 4, 5, 6 };

auto it = v.begin();
int i = *it; // i is 4
++it;
i = *it; // i is 5
*it = 6; // v contains { 4, 6, 6 }
auto e = v.end(); // e points to the element after the end of v. It can be
// used to check whether an iterator reached the end of the vector:
++it;
it == v.end(); // false, it points to the element at position 2 (with value 6)
++it;

Complete C++ Tips & Secrets for Professionals 112


it == v.end(); // true

It is consistent with the standard that a std::vector<T>'s iterators actually be T*s, but most standard libraries do
not do this. Not doing this both improves error messages, catches non-portable code, and can be used to
instrument the iterators with debugging checks in non-release builds. Then, in release builds, the class wrapping
around the underlying pointer is optimized away.

You can persist a reference or a pointer to an element of a vector for indirect access. These references or pointers
to elements in the vector remain stable and access remains defined unless you add/remove elements at or before
the element in the vector, or you cause the vector capacity to change. This is the same as the rule for invalidating
iterators.

Version≥C++11
std::vector<int> v{ 1, 2, 3 };
int* p = v.data() + 1; // p points to 2
v.insert(v.begin(), 0); // p is now invalid, accessing *p is a undefined behavior.
p = v.data() + 1; // p points to 1
v.reserve(10); // p is now invalid, accessing *p is a undefined behavior.
p = v.data() + 1; // p points to 1
v.erase(v.begin()); // p is now invalid, accessing *p is a undefined behavior.

Section 12.2: Initializing a std::vector


A std::vector can be initialized in several ways while declaring it:

Version≥C++11
std::vector<int> v{ 1, 2, 3 }; // v becomes {1, 2, 3}

// Different from std::vector<int> v(3, 6)


std::vector<int> v{ 3, 6 }; // v becomes {3, 6}
// Different from std::vector<int> v{3, 6} in C++11
std::vector<int> v(3, 6); // v becomes {6, 6, 6}

std::vector<int> v(4); // v becomes {0, 0, 0, 0}

A vector can be initialized from another container in several ways:

Copy construction (from another vector only), which copies data from v2:

std::vector<int> v(v2);
std::vector<int> v = v2;
Version≥C++11

Move construction (from another vector only), which moves data from v2:

std::vector<int> v(std::move(v2));
std::vector<int> v = std::move(v2);

Iterator (range) copy-construction, which copies elements into v:

// from another vector


std::vector<int> v(v2.begin(), v2.begin() + 3); // v becomes {v2[0], v2[1], v2[2]}

// from an array
int z[] = { 1, 2, 3, 4 };
std::vector<int> v(z, z + 3); // v becomes {1, 2, 3}

// from a list
std::list<int> list1{ 1, 2, 3 };
std::vector<int> v(list1.begin(), list1.end()); // v becomes {1, 2, 3}
Version≥C++11

Complete C++ Tips & Secrets for Professionals 113


Iterator move-construction, using std::make_move_iterator, which moves elements into v:

// from another vector


std::vector<int> v(std::make_move_iterator(v2.begin()),
std::make_move_iterator(v2.end());

// from a list
std::list<int> list1{ 1, 2, 3 };
std::vector<int> v(std::make_move_iterator(list1.begin()),
std::make_move_iterator(list1.end()));

With the help of the assign() member function, a std::vector can be reinitialized after its construction:

v.assign(4, 100); // v becomes {100, 100, 100, 100}

v.assign(v2.begin(), v2.begin() + 3); // v becomes {v2[0], v2[1], v2[2]}

int z[] = { 1, 2, 3, 4 };
v.assign(z + 1, z + 4); // v becomes {2, 3, 4}

Section 12.3: Deleting Elements


Deleting the last element:
std::vector<int> v{ 1, 2, 3 };
v.pop_back(); // v becomes {1, 2}
Deleting all elements:
std::vector<int> v{ 1, 2, 3 };
v.clear(); // v becomes an empty vector
Deleting element by index:
std::vector<int> v{ 1, 2, 3, 4, 5, 6 };
v.erase(v.begin() + 3); // v becomes {1, 2, 3, 5, 6}

Note: For a vector deleting an element which is not the last element, all elements beyond the deleted element have
to be copied or moved to fill the gap, see the note below and std::list.

Deleting all elements in a range:


std::vector<int> v{ 1, 2, 3, 4, 5, 6 };
v.erase(v.begin() + 1, v.begin() + 5); // v becomes {1, 6}

Note: The above methods do not change the capacity of the vector, only the size. See Vector Size and Capacity.

The erase method, which removes a range of elements, is often used as a part of the erase-remove idiom. That is,
first std::remove moves some elements to the end of the vector, and then erase chops them off. This is a relatively
inefficient operation for any indices less than the last index of the vector because all elements after the erased
segments must be relocated to new positions. For speed critical applications that require efficient removal of
arbitrary elements in a container, see std::list.

Deleting elements by value:


std::vector<int> v{ 1, 1, 2, 2, 3, 3 };
int value_to_remove = 2;
v.erase(std::remove(v.begin(), v.end(), value_to_remove), v.end()); // v becomes {1, 1, 3, 3}
Deleting elements by condition:
// std::remove_if needs a function, that takes a vector element as argument and returns true,
// if the element shall be removed
bool _predicate(const int& element) {
return (element > 3); // This will cause all elements to be deleted that are larger than 3
}
...
std::vector<int> v{ 1, 2, 3, 4, 5, 6 };

Complete C++ Tips & Secrets for Professionals 114


v.erase(std::remove_if(v.begin(), v.end(), _predicate), v.end()); // v becomes {1, 2, 3}
Deleting elements by lambda, without creating additional predicate function
Version≥C++11
std::vector<int> v{ 1, 2, 3, 4, 5, 6 };
v.erase(std::remove_if(v.begin(), v.end(),
[](auto& element){return element > 3;} ), v.end()
);
Deleting elements by condition from a loop:
std::vector<int> v{ 1, 2, 3, 4, 5, 6 };
std::vector<int>::iterator it = v.begin();
while (it != v.end()) {
if (condition)
it = v.erase(it); // after erasing, 'it' will be set to the next element in v
else
++it; // manually set 'it' to the next element in v
}

While it is important not to increment it in case of a deletion, you should consider using a different method when
then erasing repeatedly in a loop. Consider remove_if for a more efficient way.

Deleting elements by condition from a reverse loop:


std::vector<int> v{ -1, 0, 1, 2, 3, 4, 5, 6 };
typedef std::vector<int>::reverse_iterator rev_itr;
rev_itr it = v.rbegin();

while (it != v.rend()) { // after the loop only '0' will be in v


int value = *it;
if (value) {
++it;
// See explanation below for the following line.
it = rev_itr(v.erase(it.base()));
} else
++it;
}

Note some points for the preceding loop:

Given a reverse iterator it pointing to some element, the method base gives the regular (non-reverse)
iterator pointing to the same element.

vector::erase(iterator) erases the element pointed to by an iterator, and returns an iterator to the
element that followed the given element.

reverse_iterator::reverse_iterator(iterator) constructs a reverse iterator from an iterator.

Put altogether, the line it = rev_itr(v.erase(it.base())) says: take the reverse iterator it, have v erase the
element pointed by its regular iterator; take the resulting iterator, construct a reverse iterator from it, and assign it
to the reverse iterator it.

Deleting all elements using v.clear() does not free up memory (capacity() of the vector remains unchanged). To
reclaim space, use:

std::vector<int>().swap(v);
Version≥C++11

shrink_to_fit() frees up unused vector capacity:

Complete C++ Tips & Secrets for Professionals 115


v.shrink_to_fit();

The shrink_to_fit does not guarantee to really reclaim space, but most current implementations do.

Section 12.4: Iterating Over std::vector


You can iterate over a std::vector in several ways. For each of the following sections, v is defined as follows:

std::vector<int> v;
Iterating in the Forward Direction
Version≥C++11
// Range based for
for(const auto& value: v) {
std::cout << value << "\n";
}

// Using a for loop with iterator


for(auto it = std::begin(v); it != std::end(v); ++it) {
std::cout << *it << "\n";
}

// Using for_each algorithm, using a function or functor:


void fun(int const& value) {
std::cout << value << "\n";
}

std::for_each(std::begin(v), std::end(v), fun);

// Using for_each algorithm. Using a lambda:


std::for_each(std::begin(v), std::end(v), [](int const& value) {
std::cout << value << "\n";
});
Version<C++11
// Using a for loop with iterator
for(std::vector<int>::iterator it = std::begin(v); it != std::end(v); ++it) {
std::cout << *it << "\n";
}
// Using a for loop with index
for(std::size_t i = 0; i < v.size(); ++i) {
std::cout << v[i] << "\n";
}
Iterating in the Reverse Direction
Version≥C++14
// There is no standard way to use range based for for this.
// See below for alternatives.

// Using for_each algorithm


// Note: Using a lambda for clarity. But a function or functor will work
std::for_each(std::rbegin(v), std::rend(v), [](auto const& value) {
std::cout << value << "\n";
});

// Using a for loop with iterator


for(auto rit = std::rbegin(v); rit != std::rend(v); ++rit) {
std::cout << *rit << "\n";
}
// Using a for loop with index
for(std::size_t i = 0; i < v.size(); ++i) {
std::cout << v[v.size() - 1 - i] << "\n";
}

Though there is no built-in way to use the range based for to reverse iterate; it is relatively simple to fix this. The

Complete C++ Tips & Secrets for Professionals 116


range based for uses begin() and end() to get iterators and thus simulating this with a wrapper object can achieve
the results we require.

Version≥C++14
template<class C>
struct ReverseRange {
C c; // could be a reference or a copy, if the original was a temporary
ReverseRange(C&& cin): c(std::forward<C>(cin)) {}
ReverseRange(ReverseRange&&)=default;
ReverseRange& operator=(ReverseRange&&)=delete;
auto begin() const {return std::rbegin(c);}
auto end() const {return std::rend(c);}
};
// C is meant to be deduced, and perfect forwarded into
template<class C>
ReverseRange<C> make_ReverseRange(C&& c) {return {std::forward<C>(c)};}

int main() {
std::vector<int> v { 1,2,3,4};
for(auto const& value: make_ReverseRange(v)) {
std::cout << value << "\n";
}
}
Enforcing const elements

Since C++11 the cbegin() and cend() methods allow you to obtain a constant iterator for a vector, even if the vector
is non-const. A constant iterator allows you to read but not modify the contents of the vector which is useful to
enforce const correctness:

Version≥C++11
// forward iteration
for (auto pos = v.cbegin(); pos != v.cend(); ++pos) {
// type of pos is vector<T>::const_iterator
// *pos = 5; // Compile error - can't write via const iterator
}

// reverse iteration
for (auto pos = v.crbegin(); pos != v.crend(); ++pos) {
// type of pos is vector<T>::const_iterator
// *pos = 5; // Compile error - can't write via const iterator
}

// expects Functor::operand()(T&)
for_each(v.begin(), v.end(), Functor());

// expects Functor::operand()(const T&)


for_each(v.cbegin(), v.cend(), Functor())
Version≥C++17

as_const extends this to range iteration:

for (auto const& e : std::as_const(v)) {


std::cout << e << '\n';
}

This is easy to implement in earlier versions of C++:

Version≥C++14
template <class T>
constexpr std::add_const_t<T>& as_const(T& t) noexcept {
return t;

Complete C++ Tips & Secrets for Professionals 117


}
A Note on Efficiency

Since the class std::vector is basically a class that manages a dynamically allocated contiguous array, the same
principle explained here applies to C++ vectors. Accessing the vector's content by index is much more efficient
when following the row-major order principle. Of course, each access to the vector also puts its management
content into the cache as well, but as has been debated many times (notably here and here), the difference in
performance for iterating over a std::vector compared to a raw array is negligible. So the same principle of
efficiency for raw arrays in C also applies for C++'s std::vector.

Section 12.5: vector<bool>: The Exception To So Many, So Many Rules


The standard (section 23.3.7) specifies that a specialization of vector<bool> is provided, which optimizes space by
packing the bool values, so that each takes up only one bit. Since bits aren't addressable in C++, this means that
several requirements on vector are not placed on vector<bool>:

The data stored is not required to be contiguous, so a vector<bool> can't be passed to a C API which expects
a bool array.
at(), operator [], and dereferencing of iterators do not return a reference to bool. Rather they return a
proxy object that (imperfectly) simulates a reference to a bool by overloading its assignment operators. As an
example, the following code may not be valid for std::vector<bool>, because dereferencing an iterator
does not return a reference:

Version≥C++11
std::vector<bool> v = {true, false};
for (auto &b: v) { } // error

Similarly, functions expecting a bool& argument cannot be used with the result of operator [] or at() applied to
vector<bool>, or with the result of dereferencing its iterator:

void f(bool& b);


f(v[0]); // error
f(*v.begin()); // error

The implementation of std::vector<bool> is dependent on both the compiler and architecture. The specialisation
is implemented by packing n Booleans into the lowest addressable section of memory. Here, n is the size in bits of
the lowest addressable memory. In most modern systems this is 1 byte or 8 bits. This means that one byte can
store 8 Boolean values. This is an improvement over the traditional implementation where 1 Boolean value is
stored in 1 byte of memory.

Note: The below example shows possible bitwise values of individual bytes in a traditional vs. optimized
vector<bool>. This will not always hold true in all architectures. It is, however, a good way of visualising the
optimization. In the below examples a byte is represented as [x, x, x, x, x, x, x, x].

Traditional std::vector<char> storing 8 Boolean values:

Version≥C++11
std::vector<char> trad_vect = {true, false, false, false, true, false, true, true};

Bitwise representation:

[0,0,0,0,0,0,0,1], [0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0],


[0,0,0,0,0,0,0,1], [0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,1], [0,0,0,0,0,0,0,1]

Specialized std::vector<bool> storing 8 Boolean values:

Version≥C++11

Complete C++ Tips & Secrets for Professionals 118


std::vector<bool> optimized_vect = {true, false, false, false, true, false, true, true};

Bitwise representation:

[1,0,0,0,1,0,1,1]

Notice in the above example, that in the traditional version of std::vector<bool>, 8 Boolean values take up 8 bytes
of memory, whereas in the optimized version of std::vector<bool>, they only use 1 byte of memory. This is a
significant improvement on memory usage. If you need to pass a vector<bool> to an C-style API, you may need to
copy the values to an array, or find a better way to use the API, if memory and performance are at risk.

Section 12.6: Inserting Elements


Appending an element at the end of a vector (by copying/moving):

struct Point {
double x, y;
Point(double x, double y) : x(x), y(y) {}
};
std::vector<Point> v;
Point p(10.0, 2.0);
v.push_back(p); // p is copied into the vector.
Version≥C++11

Appending an element at the end of a vector by constructing the element in place:

std::vector<Point> v;
v.emplace_back(10.0, 2.0); // The arguments are passed to the constructor of the
// given type (here Point). The object is constructed
// in the vector, avoiding a copy.

Note that std::vector does not have a push_front() member function due to performance reasons. Adding an
element at the beginning causes all existing elements in the vector to be moved. If you want to frequently insert
elements at the beginning of your container, then you might want to use std::list or std::deque instead.

Inserting an element at any position of a vector:

std::vector<int> v{ 1, 2, 3 };
v.insert(v.begin(), 9); // v now contains {9, 1, 2, 3}
Version≥C++11

Inserting an element at any position of a vector by constructing the element in place:

std::vector<int> v{ 1, 2, 3 };
v.emplace(v.begin()+1, 9); // v now contains {1, 9, 2, 3}

Inserting another vector at any position of the vector:

std::vector<int> v(4); // contains: 0, 0, 0, 0


std::vector<int> v2(2, 10); // contains: 10, 10
v.insert(v.begin()+2, v2.begin(), v2.end()); // contains: 0, 0, 10, 10, 0, 0

Inserting an array at any position of a vector:

std::vector<int> v(4); // contains: 0, 0, 0, 0


int a [] = {1, 2, 3}; // contains: 1, 2, 3
v.insert(v.begin()+1, a, a+sizeof(a)/sizeof(a[0])); // contains: 0, 1, 2, 3, 0, 0, 0

Use reserve() before inserting multiple elements if resulting vector size is known beforehand to avoid multiple
reallocations (see vector size and capacity):

Complete C++ Tips & Secrets for Professionals 119


std::vector<int> v;
v.reserve(100);
for(int i = 0; i < 100; ++i)
v.emplace_back(i);

Be sure to not make the mistake of calling resize() in this case, or you will inadvertently create a vector with 200
elements where only the latter one hundred will have the value you intended.

Section 12.7: Using std::vector as a C array


There are several ways to use a std::vector as a C array (for example, for compatibility with C libraries). This is
possible because the elements in a vector are stored contiguously.

Version≥C++11
std::vector<int> v{ 1, 2, 3 };
int* p = v.data();

In contrast to solutions based on previous C++ standards (see below), the member function .data() may also be
applied to empty vectors, because it doesn't cause undefined behavior in this case.

Before C++11, you would take the address of the vector's first element to get an equivalent pointer, if the vector
isn't empty, these both methods are interchangeable:

int* p = &v[0]; // combine subscript operator and 0 literal

int* p = &v.front(); // explicitly reference the first element

Note: If the vector is empty, v[0] and v.front() are undefined and cannot be used.

When storing the base address of the vector's data, note that many operations (such as push_back, resize, etc.) can
change the data memory location of the vector, thus invalidating previous data pointers. For example:

std::vector<int> v;
int* p = v.data();
v.resize(42); // internal memory location changed; value of p is now invalid

Section 12.8: Finding an Element in std::vector


The function std::find, defined in the <algorithm> header, can be used to find an element in a std::vector.

std::find uses the operator== to compare elements for equality. It returns an iterator to the first element in the
range that compares equal to the value.

If the element in question is not found, std::find returns std::vector::end (or std::vector::cend if the vector is
const).

Version<C++11
static const int arr[] = {5, 4, 3, 2, 1};
std::vector<int> v (arr, arr + sizeof(arr) / sizeof(arr[0]) );

std::vector<int>::iterator it = std::find(v.begin(), v.end(), 4);


std::vector<int>::difference_type index = std::distance(v.begin(), it);
// `it` points to the second element of the vector, `index` is 1

std::vector<int>::iterator missing = std::find(v.begin(), v.end(), 10);


std::vector<int>::difference_type index_missing = std::distance(v.begin(), missing);
// `missing` is v.end(), `index_missing` is 5 (ie. size of the vector)
Version≥C++11
std::vector<int> v { 5, 4, 3, 2, 1 };

Complete C++ Tips & Secrets for Professionals 120


auto it = std::find(v.begin(), v.end(), 4);
auto index = std::distance(v.begin(), it);
// `it` points to the second element of the vector, `index` is 1

auto missing = std::find(v.begin(), v.end(), 10);


auto index_missing = std::distance(v.begin(), missing);
// `missing` is v.end(), `index_missing` is 5 (ie. size of the vector)

If you need to perform many searches in a large vector, then you may want to consider sorting the vector first,
before using the binary_search algorithm.

To find the first element in a vector that satisfies a condition, std::find_if can be used. In addition to the two
parameters given to std::find, std::find_if accepts a third argument which is a function object or function
pointer to a predicate function. The predicate should accept an element from the container as an argument and
return a value convertible to bool, without modifying the container:

Version<C++11
bool isEven(int val) {
return (val % 2 == 0);
}

struct moreThan {
moreThan(int limit) : _limit(limit) {}

bool operator()(int val) {


return val > _limit;
}

int _limit;
};

static const int arr[] = {1, 3, 7, 8};


std::vector<int> v (arr, arr + sizeof(arr) / sizeof(arr[0]) );

std::vector<int>::iterator it = std::find_if(v.begin(), v.end(), isEven);


// `it` points to 8, the first even element

std::vector<int>::iterator missing = std::find_if(v.begin(), v.end(), moreThan(10));


// `missing` is v.end(), as no element is greater than 10
Version≥C++11
// find the first value that is even
std::vector<int> v = {1, 3, 7, 8};
auto it = std::find_if(v.begin(), v.end(), [](int val){return val % 2 == 0;});
// `it` points to 8, the first even element

auto missing = std::find_if(v.begin(), v.end(), [](int val){return val > 10;});


// `missing` is v.end(), as no element is greater than 10

Section 12.9: Concatenating Vectors


One std::vector can be append to another by using the member function insert():

std::vector<int> a = {0, 1, 2, 3, 4};


std::vector<int> b = {5, 6, 7, 8, 9};

a.insert(a.end(), b.begin(), b.end());

However, this solution fails if you try to append a vector to itself, because the standard specifies that iterators given
to insert() must not be from the same range as the receiver object's elements.

Version≥c++11

Complete C++ Tips & Secrets for Professionals 121


Instead of using the vector's member functions, the functions std::begin() and std::end() can be used:

a.insert(std::end(a), std::begin(b), std::end(b));

This is a more general solution, for example, because b can also be an array. However, also this solution doesn't
allow you to append a vector to itself.

If the order of the elements in the receiving vector doesn't matter, considering the number of elements in each
vector can avoid unnecessary copy operations:

if (b.size() < a.size())


a.insert(a.end(), b.begin(), b.end());
else
b.insert(b.end(), a.begin(), a.end());

Section 12.10: Matrices Using Vectors


Vectors can be used as a 2D matrix by defining them as a vector of vectors.

A matrix with 3 rows and 4 columns with each cell initialised as 0 can be defined as:

std::vector<std::vector<int> > matrix(3, std::vector<int>(4));


Version≥C++11

The syntax for initializing them using initialiser lists or otherwise are similar to that of a normal vector.

std::vector<std::vector<int>> matrix = { {0,1,2,3},


{4,5,6,7},
{8,9,10,11}
};

Values in such a vector can be accessed similar to a 2D array

int var = matrix[0][2];

Iterating over the entire matrix is similar to that of a normal vector but with an extra dimension.

for(int i = 0; i < 3; ++i)


{
for(int j = 0; j < 4; ++j)
{
std::cout << matrix[i][j] << std::endl;
}
}
Version≥C++11
for(auto& row: matrix)
{
for(auto& col : row)
{
std::cout << col << std::endl;
}
}

A vector of vectors is a convenient way to represent a matrix but it's not the most efficient: individual vectors are
scattered around memory and the data structure isn't cache friendly.

Also, in a proper matrix, the length of every row must be the same (this isn't the case for a vector of vectors). The
additional flexibility can be a source of errors.

Complete C++ Tips & Secrets for Professionals 122


Section 12.11: Using a Sorted Vector for Fast Element Lookup
The <algorithm> header provides a number of useful functions for working with sorted vectors.

An important prerequisite for working with sorted vectors is that the stored values are comparable with <.

An unsorted vector can be sorted by using the function std::sort():

std::vector<int> v;
// add some code here to fill v with some elements
std::sort(v.begin(), v.end());

Sorted vectors allow efficient element lookup using the function std::lower_bound(). Unlike std::find(), this
performs an efficient binary search on the vector. The downside is that it only gives valid results for sorted input
ranges:

// search the vector for the first element with value 42


std::vector<int>::iterator it = std::lower_bound(v.begin(), v.end(), 42);
if (it != v.end() && *it == 42) {
// we found the element!
}

Note: If the requested value is not part of the vector, std::lower_bound() will return an iterator to the first element
that is greater than the requested value. This behavior allows us to insert a new element at its right place in an
already sorted vector:

int const new_element = 33;


v.insert(std::lower_bound(v.begin(), v.end(), new_element), new_element);

If you need to insert a lot of elements at once, it might be more efficient to call push_back() for all them first and
then call std::sort() once all elements have been inserted. In this case, the increased cost of the sorting can pay
off against the reduced cost of inserting new elements at the end of the vector and not in the middle.

If your vector contains multiple elements of the same value, std::lower_bound() will try to return an iterator to the
first element of the searched value. However, if you need to insert a new element after the last element of the
searched value, you should use the function std::upper_bound() as this will cause less shifting around of
elements:

v.insert(std::upper_bound(v.begin(), v.end(), new_element), new_element);

If you need both the upper bound and the lower bound iterators, you can use the function std::equal_range() to
retrieve both of them efficiently with one call:

std::pair<std::vector<int>::iterator,
std::vector<int>::iterator> rg = std::equal_range(v.begin(), v.end(), 42);
std::vector<int>::iterator lower_bound = rg.first;
std::vector<int>::iterator upper_bound = rg.second;

In order to test whether an element exists in a sorted vector (although not specific to vectors), you can use the
function std::binary_search():

bool exists = std::binary_search(v.begin(), v.end(), value_to_find);

Section 12.12: Reducing the Capacity of a Vector


A std::vector automatically increases its capacity upon insertion as needed, but it never reduces its capacity after
element removal.

// Initialize a vector with 100 elements

Complete C++ Tips & Secrets for Professionals 123


std::vector<int> v(100);

// The vector's capacity is always at least as large as its size


auto const old_capacity = v.capacity();
// old_capacity >= 100

// Remove half of the elements


v.erase(v.begin() + 50, v.end()); // Reduces the size from 100 to 50 (v.size() == 50),
// but not the capacity (v.capacity() == old_capacity)

To reduce its capacity, we can copy the contents of a vector to a new temporary vector. The new vector will have the
minimum capacity that is needed to store all elements of the original vector. If the size reduction of the original
vector was significant, then the capacity reduction for the new vector is likely to be significant. We can then swap
the original vector with the temporary one to retain its minimized capacity:

std::vector<int>(v).swap(v);
Version≥C++11

In C++11 we can use the shrink_to_fit() member function for a similar effect:

v.shrink_to_fit();

Note: The shrink_to_fit() member function is a request and doesn't guarantee to reduce capacity.

Section 12.13: Vector size and capacity


Vector size is simply the number of elements in the vector:

1. Current vector size is queried by size() member function. Convenience empty() function returns true if size
is 0:

vector<int> v = { 1, 2, 3 }; // size is 3
const vector<int>::size_type size = v.size();
cout << size << endl; // prints 3
cout << boolalpha << v.empty() << endl; // prints false

2. Default constructed vector starts with a size of 0:

vector<int> v; // size is 0
cout << v.size() << endl; // prints 0

3. Adding N elements to vector increases size by N (e.g. by push_back(), insert() or resize() functions).

4. Removing N elements from vector decreases size by N (e.g. by pop_back(), erase() or clear() functions).

5. Vector has an implementation-specific upper limit on its size, but you are likely to run out of RAM before
reaching it:

vector<int> v;
const vector<int>::size_type max_size = v.max_size();
cout << max_size << endl; // prints some large number
v.resize( max_size ); // probably won't work
v.push_back( 1 ); // definitely won't work

Common mistake: size is not necessarily (or even usually) int:

// !!!bad!!!evil!!!

Complete C++ Tips & Secrets for Professionals 124


vector<int> v_bad( N, 1 ); // constructs large N size vector
for( int i = 0; i < v_bad.size(); ++i ) { // size is not supposed to be int!
do_something( v_bad[i] );
}

Vector capacity differs from size. While size is simply how many elements the vector currently has, capacity is for
how many elements it allocated/reserved memory for. That is useful, because too frequent (re)allocations of too
large sizes can be expensive.

1. Current vector capacity is queried by capacity() member function. Capacity is always greater or equal to
size:

vector<int> v = { 1, 2, 3 }; // size is 3, capacity is >= 3


const vector<int>::size_type capacity = v.capacity();
cout << capacity << endl; // prints number >= 3

2. You can manually reserve capacity by reserve( N ) function (it changes vector capacity to N):

// !!!bad!!!evil!!!
vector<int> v_bad;
for( int i = 0; i < 10000; ++i ) {
v_bad.push_back( i ); // possibly lot of reallocations
}

// good
vector<int> v_good;
v_good.reserve( 10000 ); // good! only one allocation
for( int i = 0; i < 10000; ++i ) {
v_good.push_back( i ); // no allocations needed anymore
}

3. You can request for the excess capacity to be released by shrink_to_fit() (but the implementation doesn't
have to obey you). This is useful to conserve used memory:

vector<int> v = { 1, 2, 3, 4, 5 }; // size is 5, assume capacity is 6


v.shrink_to_fit(); // capacity is 5 (or possibly still 6)
cout << boolalpha << v.capacity() == v.size() << endl; // prints likely true (but possibly
false)

Vector partly manages capacity automatically, when you add elements it may decide to grow. Implementers like to
use 2 or 1.5 for the grow factor (golden ratio would be the ideal value - but is impractical due to being rational
number). On the other hand vector usually do not automatically shrink. For example:

vector<int> v; // capacity is possibly (but not guaranteed) to be 0


v.push_back( 1 ); // capacity is some starter value, likely 1
v.clear(); // size is 0 but capacity is still same as before!

v = { 1, 2, 3, 4 }; // size is 4, and lets assume capacity is 4.


v.push_back( 5 ); // capacity grows - let's assume it grows to 6 (1.5 factor)
v.push_back( 6 ); // no change in capacity
v.push_back( 7 ); // capacity grows - let's assume it grows to 9 (1.5 factor)
// and so on
v.pop_back(); v.pop_back(); v.pop_back(); v.pop_back(); // capacity stays the same

Section 12.14: Iterator/Pointer Invalidation


Iterators and pointers pointing into an std::vector can become invalid, but only when performing certain
operations. Using invalid iterators/pointers will result in undefined behavior.

Complete C++ Tips & Secrets for Professionals 125


Operations which invalidate iterators/pointers include:

Any insertion operation which changes the capacity of the vector will invalidate all iterators/pointers:

vector<int> v(5); // Vector has a size of 5; capacity is unknown.


int *p1 = &v[0];
v.push_back(2); // p1 may have been invalidated, since the capacity was unknown.

v.reserve(20); // Capacity is now at least 20.


int *p2 = &v[0];
v.push_back(4); // p2 is *not* invalidated, since the size of `v` is now 7.
v.insert(v.end(), 30, 9); // Inserts 30 elements at the end. The size exceeds the
// requested capacity of 20, so `p2` is (probably) invalidated.
int *p3 = &v[0];
v.reserve(v.capacity() + 20); // Capacity exceeded, thus `p3` is invalid.

Version≥C++11
auto old_cap = v.capacity();
v.shrink_to_fit();
if(old_cap != v.capacity())
// Iterators were invalidated.

Any insertion operation, which does not increase the capacity, will still invalidate iterators/pointers pointing
to elements at the insertion position and past it. This includes the end iterator:

vector<int> v(5);
v.reserve(20); // Capacity is at least 20.
int *p1 = &v[0];
int *p2 = &v[3];
v.insert(v.begin() + 2, 5, 0); // `p2` is invalidated, but since the capacity
// did not change, `p1` remains valid.
int *p3 = &v[v.size() - 1];
v.push_back(10); // The capacity did not change, so `p3` and `p1` remain valid.

Any removal operation will invalidate iterators/pointers pointing to the removed elements and to any
elements past the removed elements. This includes the end iterator:

vector<int> v(10);
int *p1 = &v[0];
int *p2 = &v[5];
v.erase(v.begin() + 3, v.end()); // `p2` is invalid, but `p1` remains valid.

operator= (copy, move, or otherwise) and clear() will invalidate all iterators/pointers pointing into the
vector.

Section 12.15: Find max and min Element and Respective Index in a Vector
To find the largest or smallest element stored in a vector, you can use the methods std::max_element and
std::min_element, respectively. These methods are defined in <algorithm> header. If several elements are
equivalent to the greatest (smallest) element, the methods return the iterator to the first such element. Return
v.end() for empty vectors.

std::vector<int> v = {5, 2, 8, 10, 9};


int maxElementIndex = std::max_element(v.begin(),v.end()) - v.begin();
int maxElement = *std::max_element(v.begin(), v.end());

int minElementIndex = std::min_element(v.begin(),v.end()) - v.begin();


int minElement = *std::min_element(v.begin(), v.end());

Complete C++ Tips & Secrets for Professionals 126


std::cout << "maxElementIndex:" << maxElementIndex << ", maxElement:" << maxElement << '\n';
std::cout << "minElementIndex:" << minElementIndex << ", minElement:" << minElement << '\n';

Output:

maxElementIndex:3, maxElement:10
minElementIndex:1, minElement:2

Version≥C++11

The minimum and maximum element in a vector can be retrieved at the same time by using the method
std::minmax_element, which is also defined in <algorithm> header:

std::vector<int> v = {5, 2, 8, 10, 9};


auto minmax = std::minmax_element(v.begin(), v.end());

std::cout << "minimum element: " << *minmax.first << '\n';


std::cout << "maximum element: " << *minmax.second << '\n';

Output:

minimum element: 2
maximum element: 10

Section 12.16: Converting an array to std::vector


An array can easily be converted into a std::vector by using std::begin and std::end:

Version≥C++11
int values[5] = { 1, 2, 3, 4, 5 }; // source array

std::vector<int> v(std::begin(values), std::end(values)); // copy array to new vector

for(auto &x: v)
std::cout << x << " ";
std::cout << std::endl;

12345

int main(int argc, char* argv[]) {


// convert main arguments into a vector of strings.
std::vector<std::string> args(argv, argv + argc);
}

A C++11 initializer_list<> can also be used to initialize the vector at once

initializer_list<int> arr = { 1,2,3,4,5 };


vector<int> vec1 {arr};

for (auto & i : vec1)


cout << i << endl;

Section 12.17: Functions Returning Large Vectors


Version≥C++11

In C++11, compilers are required to implicitly move from a local variable that is being returned. Moreover, most
compilers can perform copy elision in many cases and elide the move altogether. As a result of this, returning large
objects that can be moved cheaply no longer requires special handling:

Complete C++ Tips & Secrets for Professionals 127


#include <vector>
#include <iostream>

// If the compiler is unable to perform named return value optimization (NRVO)


// and elide the move altogether, it is required to move from v into the return value.
std::vector<int> fillVector(int a, int b) {
std::vector<int> v;
v.reserve(b-a+1);
for (int i = a; i <= b; i++) {
v.push_back(i);
}
return v; // implicit move
}

int main() { // declare and fill vector


std::vector<int> vec = fillVector(1, 10);

// print vector
for (auto value : vec)
std::cout << value << " "; // this will print "1 2 3 4 5 6 7 8 9 10 "

std::cout << std::endl;

return 0;
}
Version<C++11

Before C++11, copy elision was already allowed and implemented by most compilers. However, due to the absence
of move semantics, in legacy code or code that has to be compiled with older compiler versions which don't
implement this optimization, you can find vectors being passed as output arguments to prevent the unneeded
copy:

#include <vector>
#include <iostream>

// passing a std::vector by reference


void fillVectorFrom_By_Ref(int a, int b, std::vector<int> &v) {
assert(v.empty());
v.reserve(b-a+1);
for (int i = a; i <= b; i++) {
v.push_back(i);
}
}

int main() {// declare vector


std::vector<int> vec;

// fill vector
fillVectorFrom_By_Ref(1, 10, vec);
// print vector
for (std::vector<int>::const_iterator it = vec.begin(); it != vec.end(); ++it)
std::cout << *it << " "; // this will print "1 2 3 4 5 6 7 8 9 10 "
std::cout << std::endl;
return 0;
}

Chapter 13: Operator Overloading


In C++, it is possible to define operators such as + and -> for user-defined types. For example, the <string> header
defines a + operator to concatenate strings. This is done by defining an operator function using the operator

Complete C++ Tips & Secrets for Professionals 128


keyword.

Section 13.1: Arithmetic operators


You can overload all basic arithmetic operators:

+ and +=
- and -=
* and *=
/ and /=
& and &=
| and |=
^ and ^=
>> and >>=
<< and <<=

Overloading for all operators is the same. Scroll down for explanation

Overloading outside of class/struct:

//operator+ should be implemented in terms of operator+=


T operator+(T lhs, const T& rhs)
{
lhs += rhs;
return lhs;
}

T& operator+=(T& lhs, const T& rhs)


{
//Perform addition
return lhs;
}

Overloading inside of class/struct:

//operator+ should be implemented in terms of operator+=


T operator+(const T& rhs)
{
*this += rhs;
return *this;
}

T& operator+=(const T& rhs)


{
//Perform addition
return *this;
}

Note: operator+ should return by non-const value, as returning a reference wouldn't make sense (it returns a new
object) nor would returning a const value (you should generally not return by const). The first argument is passed
by value, why? Because

1. You can't modify the original object (Object foobar = foo + bar; shouldn't modify foo after all, it wouldn't
make sense)
2. You can't make it const, because you will have to be able to modify the object (because operator+ is
implemented in terms of operator+=, which modifies the object)

Passing by const& would be an option, but then you will have to make a temporary copy of the passed object. By

Complete C++ Tips & Secrets for Professionals 129


passing by value, the compiler does it for you.

operator+= returns a reference to the itself, because it is then possible to chain them (don't use the same variable
though, that would be undefined behavior due to sequence points).

The first argument is a reference (we want to modify it), but not const, because then you wouldn't be able to
modify it. The second argument should not be modified, and so for performance reason is passed by const&
(passing by const reference is faster than by value).

Section 13.2: Array subscript operator


You can even overload the array subscript operator [].

You should always (99.98% of the time) implement 2 versions, a const and a not-const version, because if the
object is const, it should not be able to modify the object returned by [].

The arguments are passed by const& instead of by value because passing by reference is faster than by value, and
const so that the operator doesn't change the index accidentally.

The operators return by reference, because by design you can modify the object [] return, i.e:

std::vector<int> v{ 1 };
v[0] = 2; //Changes value of 1 to 2
//wouldn't be possible if not returned by reference

You can only overload inside a class/struct:

//I is the index type, normally an int


T& operator[](const I& index)
{
//Do something
//return something
}

//I is the index type, normally an int


const T& operator[](const I& index) const
{
//Do something
//return something
}

Multiple subscript operators, [][]..., can be achieved via proxy objects. The following example of a simple row-
major matrix class demonstrates this:

template<class T>
class matrix {
// class enabling [][] overload to access matrix elements
template <class C>
class proxy_row_vector {
using reference = decltype(std::declval<C>()[0]);
using const_reference = decltype(std::declval<C const>()[0]);
public:
proxy_row_vector(C& _vec, std::size_t _r_ind, std::size_t _cols)
: vec(_vec), row_index(_r_ind), cols(_cols) {}
const_reference operator[](std::size_t _col_index) const {
return vec[row_index*cols + _col_index];
}
reference operator[](std::size_t _col_index) {
return vec[row_index*cols + _col_index];
}

Complete C++ Tips & Secrets for Professionals 130


private:
C& vec;
std::size_t row_index; // row index to access
std::size_t cols; // number of columns in matrix
};

using const_proxy = proxy_row_vector<const std::vector<T>>;


using proxy = proxy_row_vector<std::vector<T>>;
public:
matrix() : mtx(), rows(0), cols(0) {}
matrix(std::size_t _rows, std::size_t _cols)
: mtx(_rows*_cols), rows(_rows), cols(_cols) {}

// call operator[] followed by another [] call to access matrix elements


const_proxy operator[](std::size_t _row_index) const {
return const_proxy(mtx, _row_index, cols);
}

proxy operator[](std::size_t _row_index) {


return proxy(mtx, _row_index, cols);
}
private:
std::vector<T> mtx;
std::size_t rows;
std::size_t cols;
};

Section 13.3: Conversion operators


You can overload type operators, so that your type can be implicitly converted into the specified type.

The conversion operator must be defined in a class/struct:

operator T() const { /* return something */ }

Note: the operator is const to allow const objects to be converted.

Example:

struct Text
{
std::string text;

// Now Text can be implicitly converted into a const char*


/*explicit*/ operator const char*() const { return text.data(); }
// ^^^^^^^
// to disable implicit conversion
};

Text t;
t.text = "Hello world!";

//Ok
const char* copyoftext = t;

Section 13.4: Complex Numbers Revisited


The code below implements a very simple complex number type for which the underlying field is automatically
promoted, following the language's type promotion rules, under application of the four basic operators (+, -, *, and
/) with a member of a different field (be it another complex<T> or some scalar type).

Complete C++ Tips & Secrets for Professionals 131


This is intended to be a holistic example covering operator overloading alongside basic use of templates.

#include <type_traits>

namespace not_std{

using std::decay_t;

//----------------------------------------------------------------
// complex< value_t >
//----------------------------------------------------------------

template<typename value_t>
struct complex
{
value_t x;
value_t y;

complex &operator += (const value_t &x)


{
this->x += x;
return *this;
}
complex &operator += (const complex &other)
{
this->x += other.x;
this->y += other.y;
return *this;
}

complex &operator -= (const value_t &x)


{
this->x -= x;
return *this;
}
complex &operator -= (const complex &other)
{
this->x -= other.x;
this->y -= other.y;
return *this;
}

complex &operator *= (const value_t &s)


{
this->x *= s;
this->y *= s;
return *this;
}
complex &operator *= (const complex &other)
{
(*this) = (*this) * other;
return *this;
}

complex &operator /= (const value_t &s)


{
this->x /= s;
this->y /= s;
return *this;
}
complex &operator /= (const complex &other)
{

Complete C++ Tips & Secrets for Professionals 132


(*this) = (*this) / other;
return *this;
}

complex(const value_t &x, const value_t &y)


: x{x}
, y{y}
{}

template<typename other_value_t>
explicit complex(const complex<other_value_t> &other)
: x{static_cast<const value_t &>(other.x)}
, y{static_cast<const value_t &>(other.y)}
{}

complex &operator = (const complex &) = default;


complex &operator = (complex &&) = default;
complex(const complex &) = default;
complex(complex &&) = default;
complex() = default;
};

// Absolute value squared


template<typename value_t>
value_t absqr(const complex<value_t> &z)
{ return z.x*z.x + z.y*z.y; }

//----------------------------------------------------------------
// operator - (negation)
//----------------------------------------------------------------

template<typename value_t>
complex<value_t> operator - (const complex<value_t> &z)
{ return {-z.x, -z.y}; }

//----------------------------------------------------------------
// operator +
//----------------------------------------------------------------

template<typename left_t,typename right_t>


auto operator + (const complex<left_t> &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a.x + b.x)>>
{ return{a.x + b.x, a.y + b.y}; }

template<typename left_t,typename right_t>


auto operator + (const left_t &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a + b.x)>>
{ return{a + b.x, b.y}; }

template<typename left_t,typename right_t>


auto operator + (const complex<left_t> &a, const right_t &b)
-> complex<decay_t<decltype(a.x + b)>>
{ return{a.x + b, a.y}; }

//----------------------------------------------------------------
// operator -
//----------------------------------------------------------------

template<typename left_t,typename right_t>


auto operator - (const complex<left_t> &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a.x - b.x)>>
{ return{a.x - b.x, a.y - b.y}; }

Complete C++ Tips & Secrets for Professionals 133


template<typename left_t,typename right_t>
auto operator - (const left_t &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a - b.x)>>
{ return{a - b.x, - b.y}; }

template<typename left_t,typename right_t>


auto operator - (const complex<left_t> &a, const right_t &b)
-> complex<decay_t<decltype(a.x - b)>>
{ return{a.x - b, a.y}; }

//----------------------------------------------------------------
// operator *
//----------------------------------------------------------------

template<typename left_t, typename right_t>


auto operator * (const complex<left_t> &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a.x * b.x)>>
{
return {
a.x*b.x - a.y*b.y,
a.x*b.y + a.y*b.x
};
}

template<typename left_t, typename right_t>


auto operator * (const left_t &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a * b.x)>>
{ return {a * b.x, a * b.y}; }

template<typename left_t, typename right_t>


auto operator * (const complex<left_t> &a, const right_t &b)
-> complex<decay_t<decltype(a.x * b)>>
{ return {a.x * b, a.y * b}; }

//----------------------------------------------------------------
// operator /
//----------------------------------------------------------------

template<typename left_t, typename right_t>


auto operator / (const complex<left_t> &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a.x / b.x)>>
{
const auto r = absqr(b);
return {
( a.x*b.x + a.y*b.y) / r,
(-a.x*b.y + a.y*b.x) / r
};
}

template<typename left_t, typename right_t>


auto operator / (const left_t &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a / b.x)>>
{
const auto s = a/absqr(b);
return {
b.x * s,
-b.y * s
};
}

template<typename left_t, typename right_t>


auto operator / (const complex<left_t> &a, const right_t &b)

Complete C++ Tips & Secrets for Professionals 134


-> complex<decay_t<decltype(a.x / b)>>
{ return {a.x / b, a.y / b}; }

}// namespace not_std

int main(int argc, char **argv)


{
using namespace not_std;

complex<float> fz{4.0f, 1.0f};

// makes a complex<double>
auto dz = fz * 1.0;

// still a complex<double>
auto idz = 1.0f/dz;

// also a complex<double>
auto one = dz * idz;

// a complex<double> again
auto one_again = fz * idz;

// Operator tests, just to make sure everything compiles.

complex<float> a{1.0f, -2.0f};


complex<double> b{3.0, -4.0};

// All of these are complex<double>


auto c0 = a + b;
auto c1 = a - b;
auto c2 = a * b;
auto c3 = a / b;

// All of these are complex<float>


auto d0 = a + 1;
auto d1 = 1 + a;
auto d2 = a - 1;
auto d3 = 1 - a;
auto d4 = a * 1;
auto d5 = 1 * a;
auto d6 = a / 1;
auto d7 = 1 / a;

// All of these are complex<double>


auto e0 = b + 1;
auto e1 = 1 + b;
auto e2 = b - 1;
auto e3 = 1 - b;
auto e4 = b * 1;
auto e5 = 1 * b;
auto e6 = b / 1;
auto e7 = 1 / b;

return 0;
}

Section 13.5: Named operators


You can extend C++ with named operators that are "quoted" by standard C++ operators.

Complete C++ Tips & Secrets for Professionals 135


First we start with a dozen-line library:

namespace named_operator {
template<class D>struct make_operator{constexpr make_operator(){}};

template<class T, char, class O> struct half_apply { T&& lhs; };

template<class Lhs, class Op>


half_apply<Lhs, '*', Op> operator*( Lhs&& lhs, make_operator<Op> ) {
return {std::forward<Lhs>(lhs)};
}

template<class Lhs, class Op, class Rhs>


auto operator*( half_apply<Lhs, '*', Op>&& lhs, Rhs&& rhs )
-> decltype( named_invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) ) )
{
return named_invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) );
}
}

this doesn't do anything yet.

First, appending vectors

namespace my_ns {
struct append_t : named_operator::make_operator<append_t> {};
constexpr append_t append{};

template<class T, class A0, class A1>


std::vector<T, A0> named_invoke( std::vector<T, A0> lhs, append_t, std::vector<T, A1> const& rhs
) {
lhs.insert( lhs.end(), rhs.begin(), rhs.end() );
return std::move(lhs);
}
}
using my_ns::append;

std::vector<int> a {1,2,3};
std::vector<int> b {4,5,6};

auto c = a *append* b;

The core here is that we define an append object of type append_t:named_operator::make_operator<append_t>.

We then overload named_invoke( lhs, append_t, rhs ) for the types we want on the right and left.

The library overloads lhs*append_t, returning a temporary half_apply object. It also overloads half_apply*rhs to
call named_invoke( lhs, append_t, rhs ).

We simply have to create the proper append_t token and do an ADL-friendly named_invoke of the proper signature,
and everything hooks up and works.

For a more complex example, suppose you want to have element-wise multiplication of elements of a std::array:

template<class=void, std::size_t...Is>
auto indexer( std::index_sequence<Is...> ) {
return [](auto&& f) {
return f( std::integral_constant<std::size_t, Is>{}... );
};
}
template<std::size_t N>

Complete C++ Tips & Secrets for Professionals 136


auto indexer() { return indexer( std::make_index_sequence<N>{} ); }

namespace my_ns {
struct e_times_t : named_operator::make_operator<e_times_t> {};
constexpr e_times_t e_times{};

template<class L, class R, std::size_t N,


class Out=std::decay_t<decltype( std::declval<L const&>()*std::declval<R const&>() )>
>
std::array<Out, N> named_invoke( std::array<L, N> const& lhs, e_times_t, std::array<R, N> const&
rhs ) {
using result_type = std::array<Out, N>;
auto index_over_N = indexer<N>();
return index_over_N([&](auto...is)->result_type {
return {{
(lhs[is] * rhs[is])...
}};
});
}
}

live example.

This element-wise array code can be extended to work on tuples or pairs or C-style arrays, or even variable length
containers if you decide what to do if the lengths don't match.

You could also an element-wise operator type and get lhs *element_wise<'+'>* rhs.

Writing a *dot* and *cross* product operators are also obvious uses.

The use of * can be extended to support other delimiters, like +. The delimeter precidence determines the
precidence of the named operator, which may be important when translating physics equations over to C++ with
minimal use of extra ()s.

With a slight change in the library above, we can support ->*then* operators and extend std::function prior to
the standard being updated, or write monadic ->*bind*. It could also have a stateful named operator, where we
carefully pass the Op down to the final invoke function, permitting:

named_operator<'*'> append = [](auto lhs, auto&& rhs) {


using std::begin; using std::end;
lhs.insert( end(lhs), begin(rhs), end(rhs) );
return std::move(lhs);
};

generating a named container-appending operator in C++17.

Section 13.6: Unary operators


You can overload the 2 unary operators:

++foo and foo++


--foo and foo--

Overloading is the same for both types (++ and --). Scroll down for explanation

Overloading outside of class/struct:

//Prefix operator ++foo


T& operator++(T& lhs)
{

Complete C++ Tips & Secrets for Professionals 137


//Perform addition
return lhs;
}

//Postfix operator foo++ (int argument is used to separate pre- and postfix)
//Should be implemented in terms of ++foo (prefix operator)
T operator++(T& lhs, int)
{
T t(lhs);
++lhs;
return t;
}

Overloading inside of class/struct:

//Prefix operator ++foo


T& operator++()
{
//Perform addition
return *this;
}

//Postfix operator foo++ (int argument is used to separate pre- and postfix)
//Should be implemented in terms of ++foo (prefix operator)
T operator++(int)
{
T t(*this);
++(*this);
return t;
}

Note: The prefix operator returns a reference to itself, so that you can continue operations on it. The first argument
is a reference, as the prefix operator changes the object, that's also the reason why it isn't const (you wouldn't be
able to modify it otherwise).

The postfix operator returns by value a temporary (the previous value), and so it cannot be a reference, as it would
be a reference to a temporary, which would be garbage value at the end of the function, because the temporary
variable goes out of scope). It also cannot be const, because you should be able to modify it directly.

The first argument is a non-const reference to the "calling" object, because if it were const, you wouldn't be able to
modify it, and if it weren't a reference, you wouldn't change the original value.

It is because of the copying needed in postfix operator overloads that it's better to make it a habit to use prefix ++
instead of postfix ++ in for loops. From the for loop perspective, they're usually functionally equivalent, but there
might be a slight performance advantage to using prefix ++, especially with "fat" classes with a lot of members to
copy. Example of using prefix ++ in a for loop:

for (list<string>::const_iterator it = tokens.begin();


it != tokens.end();
++it) { // Don't use it++
...
}

Section 13.7: Comparison operators


You can overload all comparison operators:

== and !=
> and <

Complete C++ Tips & Secrets for Professionals 138


>= and <=

The recommended way to overload all those operators is by implementing only 2 operators (== and <) and then
using those to define the rest. Scroll down for explanation

Overloading outside of class/struct:

//Only implement those 2


bool operator==(const T& lhs, const T& rhs) { /* Compare */ }
bool operator<(const T& lhs, const T& rhs) { /* Compare */ }

//Now you can define the rest


bool operator!=(const T& lhs, const T& rhs) { return !(lhs == rhs); }
bool operator>(const T& lhs, const T& rhs) { return rhs < lhs; }
bool operator<=(const T& lhs, const T& rhs) { return !(lhs > rhs); }
bool operator>=(const T& lhs, const T& rhs) { return !(lhs < rhs); }

Overloading inside of class/struct:

//Note that the functions are const, because if they are not const, you wouldn't be able
//to call them if the object is const

//Only implement those 2


bool operator==(const T& rhs) const { /* Compare */ }
bool operator<(const T& rhs) const { /* Compare */ }

//Now you can define the rest


bool operator!=(const T& rhs) const { return !(*this == rhs); }
bool operator>(const T& rhs) const { return rhs < *this; }
bool operator<=(const T& rhs) const { return !(*this > rhs); }
bool operator>=(const T& rhs) const { return !(*this < rhs); }

The operators obviously return a bool, indicating true or false for the corresponding operation.

All of the operators take their arguments by const&, because the only thing that does operators do is compare, so
they shouldn't modify the objects. Passing by & (reference) is faster than by value, and to make sure that the
operators don't modify it, it is a const-reference.

Note that the operators inside the class/struct are defined as const, the reason for this is that without the
functions being const, comparing const objects would not be possible, as the compiler doesn't know that the
operators don't modify anything.

Section 13.8: Assignment operator


The assignment operator is one of the most important operators because it allows you to change the status of a
variable.

If you do not overload the assigment operator for your class/struct, it is automatically generated by the compiler:
the automatically-generated assignment operator performs a "memberwise assignment", ie by invoking
assignment operators on all members, so that one object is copied to the other, a member at time. The assignment
operator should be overloaded when the simple memberwise assignment is not suitable for your class/struct, for
example if you need to perform a deep copy of an object.

Overloading the assignment operator = is easy, but you should follow some simple steps.

1. Test for self-assignment. This check is important for two reasons:


a self-assignment is a needless copy, so it does not make sense to perform it;
the next step will not work in the case of a self-assignment.

Complete C++ Tips & Secrets for Professionals 139


2. Clean the old data. The old data must be replaced with new ones. Now, you can understand the second
reason of the previous step: if the content of the object was destroyed, a self-assignment will fail to perform
the copy.
3. Copy all members. If you overload the assigment operator for your class or your struct, it is not
automatically generated by the compiler, so you will need to take charge of copying all members from the
other object.
4. Return *this. The operator returns by itself by reference, because it allows chaining (i.e. int b = (a = 6) +
4; //b == 10).

//T is some type


T& operator=(const T& other)
{
//Do something (like copying values)
return *this;
}

Note: other is passed by const&, because the object being assigned should not be changed, and passing by
reference is faster than by value, and to make sure than operator= doesn't modify it accidentally, it is const.

The assignment operator can only to be overloaded in the class/struct, because the left value of = is always the
class/struct itself. Defining it as a free function doesn't have this guarantee, and is disallowed because of that.

When you declare it in the class/struct, the left value is implicitly the class/struct itself, so there is no problem
with that.

Section 13.9: Function call operator


You can overload the function call operator ():

Overloading must be done inside of a class/struct:

//R -> Return type


//Types -> any different type
R operator()(Type name, Type2 name2, ...)
{
//Do something
//return something
}

//Use it like this (R is return type, a and b are variables)


R foo = object(a, b, ...);

For example:

struct Sum
{
int operator()(int a, int b)
{
return a + b;
}
};

//Create instance of struct


Sum sum;
int result = sum(1, 1); //result == 2

Section 13.10: Bitwise NOT operator


Overloading the bitwise NOT (~) is fairly simple. Scroll down for explanation

Complete C++ Tips & Secrets for Professionals 140


Overloading outside of class/struct:

T operator~(T lhs)
{
//Do operation
return lhs;
}

Overloading inside of class/struct:

T operator~()
{
T t(*this);
//Do operation
return t;
}

Note: operator~ returns by value, because it has to return a new value (the modified value), and not a reference to
the value (it would be a reference to the temporary object, which would have garbage value in it as soon as the
operator is done). Not const either because the calling code should be able to modify it afterwards (i.e. int a = ~a
+ 1; should be possible).

Inside the class/struct you have to make a temporary object, because you can't modify this, as it would modify
the original object, which shouldn't be the case.

Section 13.11: Bit shift operators for I/O


The operators << and >> are commonly used as "write" and "read" operators:

std::ostream overloads << to write variables to the underlying stream (example: std::cout)
std::istream overloads >> to read from the underlying stream to a variable (example: std::cin)

The way they do this is similar if you wanted to overload them "normally" outside of the class/struct, except that
specifying the arguments are not of the same type:

Return type is the stream you want to overload from (for example, std::ostream) passed by reference, to
allow chaining (Chaining: std::cout << a << b;). Example: std::ostream&
lhs would be the same as the return type
rhs is the type you want to allow overloading from (i.e. T), passed by const& instead of value for performance
reason (rhs shouldn't be changed anyway). Example: const Vector&.

Example:

//Overload std::ostream operator<< to allow output from Vector's


std::ostream& operator<<(std::ostream& lhs, const Vector& rhs)
{
lhs << "x: " << rhs.x << " y: " << rhs.y << " z: " << rhs.z << '\n';
return lhs;
}

Vector v = { 1, 2, 3};

//Now you can do


std::cout << v;

Chapter 14: Lambdas


[default-capture, capture-list] (argument-list) mutable throw-specification attributes -> return-type { lambda-body }

Complete C++ Tips & Secrets for Professionals 141


// Order of lambda specifiers and attributes.
[capture-list] (argument-list) { lambda-body } // Common lambda definition.
[=] (argument-list) { lambda-body } // Captures all needed local variables by value.
[&] (argument-list) { lambda-body } // Captures all needed local variables by reference.
[capture-list] { lambda-body } // Argument list and specifiers can be omitted.

Parameter Details
Specifies how all non-listed variables are captured. Can be = (capture by value) or & (capture by
default-capture reference). If omitted, non-listed variables are inaccessible within the lambda-body. The default-
capture must precede the capture-list.
Specifies how local variables are made accessible within the lambda-body. Variables without
prefix are captured by value. Variables prefixed with & are captured by reference. Within a class
capture-list
method, this can be used to make all its members accessible by reference. Non-listed variables
are inaccessible, unless the list is preceded by a default-capture.
argument-list Specifies the arguments of the lambda function.
(optional) Normally variables captured by value are const. Specifying mutable makes them non-
mutable
const. Changes to those variables are retained between calls.
(optional) Specifies the exception throwing behavior of the lambda function. For example:
throw-specification
noexcept or throw(std::exception).
(optional) Any attributes for the lambda function. For example, if the lambda-body always throws
attributes
an exception then [[noreturn]] can be used.
(optional) Specifies the return type of the lambda function. Required when the return type
-> return-type
cannot be determined by the compiler.
lambda-body A code block containing the implementation of the lambda function.

Section 14.1: What is a lambda expression?


A lambda expression provides a concise way to create simple function objects. A lambda expression is a prvalue
whose result object is called closure object, which behaves like a function object.

The name 'lambda expression' originates from lambda calculus, which is a mathematical formalism invented in the
1930s by Alonzo Church to investigate questions about logic and computability. Lambda calculus formed the basis
of LISP, a functional programming language. Compared to lambda calculus and LISP, C++ lambda expressions share
the properties of being unnamed, and to capture variables from the surrounding context, but they lack the ability to
operate on and return functions.

A lambda expression is often used as an argument to functions that take a callable object. That can be simpler than
creating a named function, which would be only used when passed as the argument. In such cases, lambda
expressions are generally preferred because they allow defining the function objects inline.

A lambda consists typically of three parts: a capture list [], an optional parameter list () and a body {}, all of which
can be empty:

[](){} // An empty lambda, which does and returns nothing

Capture list

[] is the capture list. By default, variables of the enclosing scope cannot be accessed by a lambda. Capturing a
variable makes it accessible inside the lambda, either as a copy or as a reference. Captured variables become a part
of the lambda; in contrast to function arguments, they do not have to be passed when calling the lambda.

int a = 0; // Define an integer variable


auto f = []() { return a*9; }; // Error: 'a' cannot be accessed
auto f = [a]() { return a*9; }; // OK, 'a' is "captured" by value
auto f = [&a]() { return a++; }; // OK, 'a' is "captured" by reference
// Note: It is the responsibility of the programmer
// to ensure that a is not destroyed before the

Complete C++ Tips & Secrets for Professionals 142


// lambda is called.
auto b = f(); // Call the lambda function. a is taken from the capture list and
not passed here.

Parameter list

() is the parameter list, which is almost the same as in regular functions. If the lambda takes no arguments, these
parentheses can be omitted (except if you need to declare the lambda mutable). These two lambdas are equivalent:

auto call_foo = [x](){ x.foo(); };


auto call_foo2 = [x]{ x.foo(); };
Version≥C++14

The parameter list can use the placeholder type auto instead of actual types. By doing so, this argument behaves
like a template parameter of a function template. Following lambdas are equivalent when you want to sort a vector
in generic code:

auto sort_cpp11 = [](std::vector<T>::const_reference lhs, std::vector<T>::const_reference rhs) {


return lhs < rhs; };
auto sort_cpp14 = [](const auto &lhs, const auto &rhs) { return lhs < rhs; };

Function body

{} is the body, which is the same as in regular functions.

Calling a lambda

A lambda expression's result object is a closure, which can be called using the operator() (as with other function
objects):

int multiplier = 5;
auto timesFive = [multiplier](int a) { return a * multiplier; };
std::out << timesFive(2); // Prints 10

multiplier = 15;
std::out << timesFive(2); // Still prints 2*5 == 10

Return Type

By default, the return type of a lambda expression is deduced.

[](){ return true; };

In this case the return type is bool.

You can also manually specify the return type using the following syntax:

[]() -> bool { return true; };

Mutable Lambda

Objects captured by value in the lambda are by default immutable. This is because the operator() of the generated
closure object is const by default.

auto func = [c = 0](){++c; std::cout << c;}; // fails to compile because ++c
// tries to mutate the state of
// the lambda.

Modifying can be allowed by using the keyword mutable, which make the closer object's operator() non-const:

Complete C++ Tips & Secrets for Professionals 143


auto func = [c = 0]() mutable {++c; std::cout << c;};

If used together with the return type, mutable comes before it.

auto func = [c = 0]() mutable -> int {++c; std::cout << c; return c;};

An example to illustrate the usefulness of lambdas

Before C++11:

Version<C++11
// Generic functor used for comparison
struct islessthan
{
islessthan(int threshold) : _threshold(threshold) {}

bool operator()(int value) const


{
return value < _threshold;
}
private:
int _threshold;
};

// Declare a vector
const int arr[] = { 1, 2, 3, 4, 5 };
std::vector<int> vec(arr, arr+5);

// Find a number that's less than a given input (assume this would have been function input)
int threshold = 10;
std::vector<int>::iterator it = std::find_if(vec.begin(), vec.end(), islessthan(threshold));

Since C++11:

Version≥C++11
// Declare a vector
std::vector<int> vec{ 1, 2, 3, 4, 5 };

// Find a number that's less than a given input (assume this would have been function input)
int threshold = 10;
auto it = std::find_if(vec.begin(), vec.end(), [threshold](int value) { return value < threshold;
});

Section 14.2: Specifying the return type


For lambdas with a single return statement, or multiple return statements whose expressions are of the same type,
the compiler can deduce the return type:

// Returns bool, because "value > 10" is a comparison which yields a Boolean result
auto l = [](int value) {
return value > 10;
}

For lambdas with multiple return statements of different types, the compiler can't deduce the return type:

// error: return types must match if lambda has unspecified return type
auto l = [](int value) {
if (value < 10) {
return 1;
} else {
return 1.5;
}

Complete C++ Tips & Secrets for Professionals 144


};

In this case you have to specify the return type explicitly:

// The return type is specified explicitly as 'double'


auto l = [](int value) -> double {
if (value < 10) {
return 1;
} else {
return 1.5;
}
};

The rules for this match the rules for auto type deduction. Lambdas without explicitly specified return types never
return references, so if a reference type is desired it must be explicitly specified as well:

auto copy = [](X& x) { return x; }; // 'copy' returns an X, so copies its input


auto ref = [](X& x) -> X& { return x; }; // 'ref' returns an X&, no copy

Section 14.3: Capture by value


If you specify the variable's name in the capture list, the lambda will capture it by value. This means that the
generated closure type for the lambda stores a copy of the variable. This also requires that the variable's type be
copy-constructible:

int a = 0;

[a]() {
return a; // Ok, 'a' is captured by value
};
Version<C++14
auto p = std::unique_ptr<T>(...);

[p]() { // Compile error; `unique_ptr` is not copy-constructible


return p->createWidget();
};

From C++14 on, it is possible to initialize variables on the spot. This allows move only types to be captured in the
lambda.

Version≥C++14
auto p = std::make_unique<T>(...);

[p = std::move(p)]() {
return p->createWidget();
};

Even though a lambda captures variables by value when they are given by their name, such variables cannot be
modified within the lambda body by default. This is because the closure type puts the lambda body in a declaration
of operator() const.

The const applies to accesses to member variables of the closure type, and captured variables that are members of
the closure (all appearances to the contrary):

int a = 0;

[a]() {
a = 2; // Illegal, 'a' is accessed via `const`

decltype(a) a1 = 1;
a1 = 2; // valid: variable 'a1' is not const

Complete C++ Tips & Secrets for Professionals 145


};

To remove the const, you have to specify the keyword mutable on the lambda:

int a = 0;

[a]() mutable {
a = 2; // OK, 'a' can be modified
return a;
};

Because a was captured by value, any modifications done by calling the lambda will not affect a. The value of a was
copied into the lambda when it was constructed, so the lambda's copy of a is separate from the external a variable.

int a = 5 ;
auto plus5Val = [a] (void) { return a + 5 ; } ;
auto plus5Ref = [&a] (void) {return a + 5 ; } ;

a = 7 ;
std::cout << a << ", value " << plus5Val() << ", reference " << plus5Ref() ;
// The result will be "7, value 10, reference 12"

Section 14.4: Recursive lambdas


Let's say we wish to write Euclid's gcd() as a lambda. As a function, it is:

int gcd(int a, int b) {


return b == 0 ? a : gcd(b, a%b);
}

But a lambda cannot be recursive, it has no way to invoke itself. A lambda has no name and using this within the
body of a lambda refers to a captured this (assuming the lambda is created in the body of a member function,
otherwise it is an error). So how do we solve this problem?

Use std::function

We can have a lambda capture a reference to a not-yet constructed std::function:

std::function<int(int, int)> gcd = [&](int a, int b){


return b == 0 ? a : gcd(b, a%b);
};

This works, but should be used sparingly. It's slow (we're using type erasure now instead of a direct function call),
it's fragile (copying gcd or returning gcd will break since the lambda refers to the original object), and it won't work
with generic lambdas.

Using two smart pointers:


auto gcd_self = std::make_shared<std::unique_ptr< std::function<int(int, int)> >>();
*gcd_self = std::make_unique<std::function<int(int, int)>>(
[gcd_self](int a, int b){
return b == 0 ? a : (**gcd_self)(b, a%b);
};
};

This adds a lot of indirection (which is overhead), but it can be copied/returned, and all copies share state. It does
let you return the lambda, and is otherwise less fragile than the above solution.

Use a Y-combinator

With the help of a short utility struct, we can solve all of these problems:

Complete C++ Tips & Secrets for Professionals 146


template <class F>
struct y_combinator {
F f; // the lambda will be stored here

// a forwarding operator():
template <class... Args>
decltype(auto) operator()(Args&&... args) const {
// we pass ourselves to f, then the arguments.
// the lambda should take the first argument as `auto&& recurse` or similar.
return f(*this, std::forward<Args>(args)...);
}
};
// helper function that deduces the type of the lambda:
template <class F>
y_combinator<std::decay_t<F>> make_y_combinator(F&& f) {
return {std::forward<F>(f)};
}
// (Be aware that in C++17 we can do better than a `make_` function)

we can implement our gcd as:

auto gcd = make_y_combinator(


[](auto&& gcd, int a, int b){
return b == 0 ? a : gcd(b, a%b);
}
);

The y_combinator is a concept from the lambda calculus that lets you have recursion without being able to name
yourself until you are defined. This is exactly the problem lambdas have.

You create a lambda that takes "recurse" as its first argument. When you want to recurse, you pass the arguments
to recurse.

The y_combinator then returns a function object that calls that function with its arguments, but with a suitable
"recurse" object (namely the y_combinator itself) as its first argument. It forwards the rest of the arguments you call
the y_combinator with to the lambda as well.

In short:

auto foo = make_y_combinator( [&](auto&& recurse, some arguments) {


// write body that processes some arguments
// when you want to recurse, call recurse(some other arguments)
});

and you have recursion in a lambda with no serious restrictions or significant overhead.

Section 14.5: Default capture


By default, local variables that are not explicitly specified in the capture list, cannot be accessed from within the
lambda body. However, it is possible to implicitly capture variables named by the lambda body:

int a = 1;
int b = 2;

// Default capture by value


[=]() { return a + b; }; // OK; a and b are captured by value

// Default capture by reference


[&]() { return a + b; }; // OK; a and b are captured by reference

Complete C++ Tips & Secrets for Professionals 147


Explicit capturing can still be done alongside implicit default capturing. The explicit capture definition will override
the default capture:

int a = 0;
int b = 1;

[=, &b]() {
a = 2; // Illegal; 'a' is capture by value, and lambda is not 'mutable'
b = 2; // OK; 'b' is captured by reference
};

Section 14.6: Class lambdas and capture of this


A lambda expression evaluated in a class' member function is implicitly a friend of that class:

class Foo
{
private:
int i;

public:
Foo(int val) : i(val) {}

// definition of a member function


void Test()
{
auto lamb = [](Foo &foo, int val)
{
// modification of a private member variable
foo.i = val;
};

// lamb is allowed to access a private member, because it is a friend of Foo


lamb(*this, 30);
}
};

Such a lambda is not only a friend of that class, it has the same access as the class it is declared within has.

Lambdas can capture the this pointer which represents the object instance the outer function was called on. This
is done by adding this to the capture list:

class Foo
{
private:
int i;

public:
Foo(int val) : i(val) {}

void Test()
{
// capture the this pointer by value
auto lamb = [this](int val)
{
i = val;
};

lamb(30);
}
};

Complete C++ Tips & Secrets for Professionals 148


When this is captured, the lambda can use member names of its containing class as though it were in its
containing class. So an implicit this-> is applied to such members.

Be aware that this is captured by value, but not the value of the type. It is captured by the value of this, which is a
pointer. As such, the lambda does not own this. If the lambda out lives the lifetime of the object that created it, the
lambda can become invalid.

This also means that the lambda can modify this without being declared mutable. It is the pointer which is const,
not the object being pointed to. That is, unless the outer member function was itself a const function.

Also, be aware that the default capture clauses, both [=] and [&], will also capture this implicitly. And they both
capture it by the value of the pointer. Indeed, it is an error to specify this in the capture list when a default is given.

Version≥C++17

Lambdas can capture a copy of the this object, created at the time the lambda is created. This is done by adding
*this to the capture list:

class Foo
{
private:
int i;

public:
Foo(int val) : i(val) {}

void Test()
{
// capture a copy of the object given by the this pointer
auto lamb = [*this](int val) mutable
{
i = val;
};

lamb(30); // does not change this->i


}
};

Section 14.7: Capture by reference


If you precede a local variable's name with an &, then the variable will be captured by reference. Conceptually, this
means that the lambda's closure type will have a reference variable, initialized as a reference to the corresponding
variable from outside of the lambda's scope. Any use of the variable in the lambda body will refer to the original
variable:

// Declare variable 'a'


int a = 0;

// Declare a lambda which captures 'a' by reference


auto set = [&a]() {
a = 1;
};

set();
assert(a == 1);

The keyword mutable is not needed, because a itself is not const.

Of course, capturing by reference means that the lambda must not escape the scope of the variables it captures.

Complete C++ Tips & Secrets for Professionals 149


So you could call functions that take a function, but you must not call a function that will store the lambda beyond
the scope of your references. And you must not return the lambda.

Section 14.8: Generic lambdas


Version≥c++14

Lambda functions can take arguments of arbitrary types. This allows a lambda to be more generic:

auto twice = [](auto x){ return x+x; };

int i = twice(2); // i == 4
std::string s = twice("hello"); // s == "hellohello"

This is implemented in C++ by making the closure type's operator() overload a template function. The following
type has equivalent behavior to the above lambda closure:

struct _unique_lambda_type
{
template<typename T>
auto operator() (T x) const {return x + x;}
};

Not all parameters in a generic lambda need be generic:

[](auto x, int y) {return x + y;}

Here, x is deduced based on the first function argument, while y will always be int.

Generic lambdas can take arguments by reference as well, using the usual rules for auto and &. If a generic
parameter is taken as auto&&, this is a forwarding reference to the passed in argument and not an rvalue reference:

auto lamb1 = [](int &&x) {return x + 5;};


auto lamb2 = [](auto &&x) {return x + 5;};
int x = 10;
lamb1(x); // Illegal; must use `std::move(x)` for `int&&` parameters.
lamb2(x); // Legal; the type of `x` is deduced as `int&`.

Lambda functions can be variadic and perfectly forward their arguments:

auto lam = [](auto&&... args){return f(std::forward<decltype(args)>(args)...);};

or:

auto lam = [](auto&&... args){return f(decltype(args)(args)...);};

which only works "properly" with variables of type auto&&.

A strong reason to use generic lambdas is for visiting syntax.

boost::variant<int, double> value;


apply_visitor(value, [&](auto&& e){
std::cout << e;
});

Here we are visiting in a polymorphic manner; but in other contexts, the names of the type we are passing isn't
interesting:

mutex_wrapped<std::ostream&> os = std::cout;
os.write([&](auto&& os){
os << "hello world\n";

Complete C++ Tips & Secrets for Professionals 150


});

Repeating the type of std::ostream& is noise here; it would be like having to mention the type of a variable every
time you use it. Here we are creating a visitor, but no a polymorphic one; auto is used for the same reason you
might use auto in a for(:) loop.

Section 14.9: Using lambdas for inline parameter pack unpacking


Version≥C++14

Parameter pack unpacking traditionally requires writing a helper function for each time you want to do it.

In this toy example:

template<std::size_t...Is>
void print_indexes( std::index_sequence<Is...> ) {
using discard=int[];
(void)discard{0,((void)(
std::cout << Is << '\n' // here Is is a compile-time constant.
),0)...};
}
template<std::size_t I>
void print_indexes_upto() {
return print_indexes( std::make_index_sequence<I>{} );
}

The print_indexes_upto wants to create and unpack a parameter pack of indexes. In order to do so, it must call a
helper function. Every time you want to unpack a parameter pack you created, you end up having to create a
custom helper function to do it.

This can be avoided with lambdas.

You can unpack parameter packs into a set of invocations of a lambda, like this:

template<std::size_t I>
using index_t = std::integral_constant<std::size_t, I>;
template<std::size_t I>
constexpr index_t<I> index{};

template<class=void, std::size_t...Is>
auto index_over( std::index_sequence<Is...> ) {
return [](auto&& f){
using discard=int[];
(void)discard{0,(void(
f( index<Is> )
),0)...};
};
}

template<std::size_t N>
auto index_over(index_t<N> = {}) {
return index_over( std::make_index_sequence<N>{} );
}
Version≥C++17

With fold expressions, index_over() can be simplified to:

template<class=void, std::size_t...Is>
auto index_over( std::index_sequence<Is...> ) {
return [](auto&& f){

Complete C++ Tips & Secrets for Professionals 151


((void)(f(index<Is>)), ...);
};
}

Once you have done that, you can use this to replace having to manually unpack parameter packs with a second
overload in other code, letting you unpack parameter packs "inline":

template<class Tup, class F>


void for_each_tuple_element(Tup&& tup, F&& f) {
using T = std::remove_reference_t<Tup>;
using std::tuple_size;
auto from_zero_to_N = index_over< tuple_size<T>{} >();

from_zero_to_N(
[&](auto i){
using std::get;
f( get<i>( std::forward<Tup>(tup) ) );
}
);
}

The auto i passed to the lambda by the index_over is a std::integral_constant<std::size_t, ???>. This has a
constexpr conversion to std::size_t that does not depend on the state of this, so we can use it as a compile-time
constant, such as when we pass it to std::get<i> above.

To go back to the toy example at the top, rewrite it as:

template<std::size_t I>
void print_indexes_upto() {
index_over(index<I>)([](auto i){
std::cout << i << '\n'; // here i is a compile-time constant
});
}

which is much shorter, and keeps logic in the code that uses it.

Live example to play with.

Section 14.10: Generalized capture


Version≥C++14

Lambdas can capture expressions, rather than just variables. This permits lambdas to store move-only types:

auto p = std::make_unique<T>(...);

auto lamb = [p = std::move(p)]() //Overrides capture-by-value of `p`.


{
p->SomeFunc();
};

This moves the outer p variable into the lambda capture variable, also called p. lamb now owns the memory
allocated by make_unique. Because the closure contains a type that is non-copyable, this means that lamb is itself
non-copyable. But it can be moved:

auto lamb_copy = lamb; //Illegal


auto lamb_move = std::move(lamb); //legal.

Now lamb_move owns the memory.

Complete C++ Tips & Secrets for Professionals 152


Note that std::function<> requires that the values stored be copyable. You can write your own move-only-
requiring std::function, or you could just stuff the lambda into a shared_ptr wrapper:

auto shared_lambda = [](auto&& f){


return [spf = std::make_shared<std::decay_t<decltype(f)>>(decltype(f)(f))]
(auto&&...args)->decltype(auto) {
return (*spf)(decltype(args)(args)...);
};
};
auto lamb_shared = shared_lambda(std::move(lamb_move));

takes our move-only lambda and stuffs its state into a shared pointer then returns a lambda that can be copied,
and then stored in a std::function or similar.

Generalized capture uses auto type deduction for the variable's type. It will declare these captures as values by
default, but they can be references as well:

int a = 0;

auto lamb = [&v = a](int add) //Note that `a` and `v` have different names
{
v += add; //Modifies `a`
};

lamb(20); //`a` becomes 20.

Generalize capture does not need to capture an external variable at all. It can capture an arbitrary expression:

auto lamb = [p = std::make_unique<T>(...)]()


{
p->SomeFunc();
}

This is useful for giving lambdas arbitrary values that they can hold and potentially modify, without having to
declare them externally to the lambda. Of course, that is only useful if you do not intend to access those variables
after the lambda has completed its work.

Section 14.11: Conversion to function pointer


If a lambda's capture list is empty, then the lambda has an implicit conversion to a function pointer that takes the
same arguments and returns the same return type:

auto sorter = [](int lhs, int rhs) -> bool {return lhs < rhs;};

using func_ptr = bool(*)(int, int);


func_ptr sorter_func = sorter; // implicit conversion

Such a conversion may also be enforced using unary plus operator:

func_ptr sorter_func2 = +sorter; // enforce implicit conversion

Calling this function pointer behaves exactly like invoking operator() on the lambda. This function pointer is in no
way reliant on the source lambda closure's existence. It therefore may outlive the lambda closure.

This feature is mainly useful for using lambdas with APIs that deal in function pointers, rather than C++ function
objects.

Version≥C++14

Complete C++ Tips & Secrets for Professionals 153


Conversion to a function pointer is also possible for generic lambdas with an empty capture list. If necessary,
template argument deduction will be used to select the correct specialization.

auto sorter = [](auto lhs, auto rhs) { return lhs < rhs; };
using func_ptr = bool(*)(int, int);
func_ptr sorter_func = sorter; // deduces int, int
// note however that the following is ambiguous
// func_ptr sorter_func2 = +sorter;

Section 14.12: Porting lambda functions to C++03 using functors


Lambda functions in C++ are syntactic sugar that provide a very concise syntax for writing functors. As such,
equivalent functionality can be obtained in C++03 (albeit much more verbose) by converting the lambda function
into a functor:

// Some dummy types:


struct T1 {int dummy;};
struct T2 {int dummy;};
struct R {int dummy;};

// Code using a lambda function (requires C++11)


R use_lambda(T1 val, T2 ref) {
// Use auto because the type of the lambda is unknown.
auto lambda = [val, &ref](int arg1, int arg2) -> R {
/* lambda-body */
return R();
};
return lambda(12, 27);
}

// The functor class (valid C++03)


// Similar to what the compiler generates for the lambda function.
class Functor {
// Capture list.
T1 val;
T2& ref;

public:
// Constructor
inline Functor(T1 val, T2& ref) : val(val), ref(ref) {}

// Functor body
R operator()(int arg1, int arg2) const {
/* lambda-body */
return R();
}
};

// Equivalent to use_lambda, but uses a functor (valid C++03).


R use_functor(T1 val, T2 ref) {
Functor functor(val, ref);
return functor(12, 27);
}

// Make this a self-contained example.


int main() {
T1 t1;
T2 t2;
use_functor(t1,t2);
use_lambda(t1,t2);
return 0;

Complete C++ Tips & Secrets for Professionals 154


}

If the lambda function is mutable then make the functor's call-operator non-const, i.e.:

R operator()(int arg1, int arg2) /*non-const*/ {


/* lambda-body */
return R();
}

Chapter 15: Loops


while (condition) statement ;
do statement while (expression) ;
for (for-init-statement ; condition ; expression) statement ;
for (for-range-declaration : for-range-initializer) statement ;
break ;
continue ;

A loop statement executes a group of statements repeatedly until a condition is met. There are 3 types of primitive
loops in C++: for, while, and do...while.

Section 15.1: Range-Based For


Version≥C++11

for loops can be used to iterate over the elements of a iterator-based range, without using a numeric index or
directly accessing the iterators:

vector<float> v = {0.4f, 12.5f, 16.234f};

for(auto val: v)
{
std::cout << val << " ";
}

std::cout << std::endl;

This will iterate over every element in v, with val getting the value of the current element. The following statement:

for (for-range-declaration : for-range-initializer ) statement

is equivalent to:

{
auto&& __range = for-range-initializer;
auto __begin = begin-expr, __end = end-expr;
for (; __begin != __end; ++__begin) {
for-range-declaration = *__begin;
statement
}
}
Version≥C++17
{
auto&& __range = for-range-initializer;
auto __begin = begin-expr;
auto __end = end-expr; // end is allowed to be a different type than begin in C++17
for (; __begin != __end; ++__begin) {
for-range-declaration = *__begin;
statement
}

Complete C++ Tips & Secrets for Professionals 155


}

This change was introduced for the planned support of Ranges TS in C++20.

In this case, our loop is equivalent to:

{
auto&& __range = v;
auto __begin = v.begin(), __end = v.end();
for (; __begin != __end; ++__begin) {
auto val = *__begin;
std::cout << val << " ";
}
}

Note that auto val declares a value type, which will be a copy of a value stored in the range (we are copy-initializing
it from the iterator as we go). If the values stored in the range are expensive to copy, you may want to use const
auto &val. You are also not required to use auto; you can use an appropriate typename, so long as it is implicitly
convertible from the range's value type.

If you need access to the iterator, range-based for cannot help you (not without some effort, at least).

If you wish to reference it, you may do so:

vector<float> v = {0.4f, 12.5f, 16.234f};

for(float &val: v)
{
std::cout << val << " ";
}

You could iterate on const reference if you have const container:

const vector<float> v = {0.4f, 12.5f, 16.234f};

for(const float &val: v)


{
std::cout << val << " ";
}

One would use forwarding references when the sequence iterator returns a proxy object and you need to operate
on that object in a non-const way. Note: it will most likely confuse readers of your code.

vector<bool> v(10);

for(auto&& val: v)
{
val = true;
}

The "range" type provided to range-based for can be one of the following:

Language arrays:

float arr[] = {0.4f, 12.5f, 16.234f};

for(auto val: arr)


{
std::cout << val << " ";

Complete C++ Tips & Secrets for Professionals 156


}

Note that allocating a dynamic array does not count:

float *arr = new float[3]{0.4f, 12.5f, 16.234f};

for(auto val: arr) //Compile error.


{
std::cout << val << " ";
}

Any type which has member functions begin() and end(), which return iterators to the elements of the type.
The standard library containers qualify, but user-defined types can be used as well:

struct Rng
{
float arr[3];

// pointers are iterators


const float* begin() const {return &arr[0];}
const float* end() const {return &arr[3];}
float* begin() {return &arr[0];}
float* end() {return &arr[3];}
};

int main()
{
Rng rng = {{0.4f, 12.5f, 16.234f}};

for(auto val: rng)


{
std::cout << val << " ";
}
}

Any type which has non-member begin(type) and end(type) functions which can found via argument
dependent lookup, based on type. This is useful for creating a range type without having to modify class type
itself:

namespace Mine
{
struct Rng {float arr[3];};

// pointers are iterators


const float* begin(const Rng &rng) {return &rng.arr[0];}
const float* end(const Rng &rng) {return &rng.arr[3];}
float* begin(Rng &rng) {return &rng.arr[0];}
float* end(Rng &rng) {return &rng.arr[3];}
}

int main()
{
Mine::Rng rng = {{0.4f, 12.5f, 16.234f}};

for(auto val: rng)


{
std::cout << val << " ";
}
}

Complete C++ Tips & Secrets for Professionals 157


Section 15.2: For loop
A for loop executes statements in the loop body, while the loop condition is true. Before the loop initialization
statement is executed exactly once. After each cycle, the iteration execution part is executed.

A for loop is defined as follows:

for (/*initialization statement*/; /*condition*/; /*iteration execution*/)


{
// body of the loop
}

Explanation of the placeholder statements:

initialization statement: This statement gets executed only once, at the beginning of the for loop. You
can enter a declaration of multiple variables of one type, such as int i = 0, a = 2, b = 3. These variables
are only valid in the scope of the loop. Variables defined before the loop with the same name are hidden
during execution of the loop.
condition: This statement gets evaluated ahead of each loop body execution, and aborts the loop if it
evaluates to false.
iteration execution: This statement gets executed after the loop body, ahead of the next condition
evaluation, unless the for loop is aborted in the body (by break, goto, return or an exception being thrown).
You can enter multiple statements in the iteration execution part, such as a++, b+=10, c=b+a.

The rough equivalent of a for loop, rewritten as a while loop is:

/*initialization*/
while (/*condition*/)
{
// body of the loop; using 'continue' will skip to increment part below
/*iteration execution*/
}

The most common case for using a for loop is to execute statements a specific number of times. For example,
consider the following:

for(int i = 0; i < 10; i++) {


std::cout << i << std::endl;
}

A valid loop is also:

for(int a = 0, b = 10, c = 20; (a+b+c < 100); c--, b++, a+=c) {


std::cout << a << " " << b << " " << c << std::endl;
}

An example of hiding declared variables before a loop is:

int i = 99; //i = 99


for(int i = 0; i < 10; i++) { //we declare a new variable i
//some operations, the value of i ranges from 0 to 9 during loop execution
}
//after the loop is executed, we can access i with value of 99

But if you want to use the already declared variable and not hide it, then omit the declaration part:

int i = 99; //i = 99


for(i = 0; i < 10; i++) { //we are using already declared variable i
//some operations, the value of i ranges from 0 to 9 during loop execution

Complete C++ Tips & Secrets for Professionals 158


}
//after the loop is executed, we can access i with value of 10

Notes:

The initialization and increment statements can perform operations unrelated to the condition statement, or
nothing at all - if you wish to do so. But for readability reasons, it is best practice to only perform operations
directly relevant to the loop.
A variable declared in the initialization statement is visible only inside the scope of the for loop and is
released upon termination of the loop.
Don't forget that the variable which was declared in the initialization statement can be modified during
the loop, as well as the variable checked in the condition.

Example of a loop which counts from 0 to 10:

for (int counter = 0; counter <= 10; ++counter)


{
std::cout << counter << '\n';
}
// counter is not accessible here (had value 11 at the end)

Explanation of the code fragments:

int counter = 0 initializes the variable counter to 0. (This variable can only be used inside of the for loop.)
counter <= 10 is a Boolean condition that checks whether counter is less than or equal to 10. If it is true,
the loop executes. If it is false, the loop ends.
++counter is an increment operation that increments the value of counter by 1 ahead of the next condition
check.

By leaving all statements empty, you can create an infinite loop:

// infinite loop
for (;;)
std::cout << "Never ending!\n";

The while loop equivalent of the above is:

// infinite loop
while (true)
std::cout << "Never ending!\n";

However, an infinite loop can still be left by using the statements break, goto, or return or by throwing an
exception.

The next common example of iterating over all elements from an STL collection (e.g., a vector) without using the
<algorithm> header is:

std::vector<std::string> names = {"Albert Einstein", "Stephen Hawking", "Michael Ellis"};


for(std::vector<std::string>::iterator it = names.begin(); it != names.end(); ++it) {
std::cout << *it << std::endl;
}

Section 15.3: While loop


A while loop executes statements repeatedly until the given condition evaluates to false. This control statement is
used when it is not known, in advance, how many times a block of code is to be executed.

For example, to print all the numbers from 0 up to 9, the following code can be used:

Complete C++ Tips & Secrets for Professionals 159


int i = 0;
while (i < 10)
{
std::cout << i << " ";
++i; // Increment counter
}
std::cout << std::endl; // End of line; "0 1 2 3 4 5 6 7 8 9" is printed to the console
Version≥C++17

Note that since C++17, the first 2 statements can be combined

while (int i = 0; i < 10)


//... The rest is the same

To create an infinite loop, the following construct can be used:

while (true)
{
// Do something forever (however, you can exit the loop by calling 'break'
}

There is another variant of while loops, namely the do...while construct. See the do-while loop example for more
information.

Section 15.4: Do-while loop


A do-while loop is very similar to a while loop, except that the condition is checked at the end of each cycle, not at
the start. The loop is therefore guaranteed to execute at least once.

The following code will print 0, as the condition will evaluate to false at the end of the first iteration:

int i =0;
do
{
std::cout << i;
++i; // Increment counter
}
while (i < 0);
std::cout << std::endl; // End of line; 0 is printed to the console

Note: Do not forget the semicolon at the end of while(condition);, which is needed in the do-while construct.

In contrast to the do-while loop, the following will not print anything, because the condition evaluates to false at
the beginning of the first iteration:

int i =0;
while (i < 0)
{
std::cout << i;
++i; // Increment counter
}
std::cout << std::endl; // End of line; nothing is printed to the console

Note: A while loop can be exited without the condition becoming false by using a break, goto, or return statement.

int i = 0;
do
{
std::cout << i;
++i; // Increment counter
if (i > 5)

Complete C++ Tips & Secrets for Professionals 160


{
break;
}
}
while (true);
std::cout << std::endl; // End of line; 0 1 2 3 4 5 is printed to the console

A trivial do-while loop is also occasionally used to write macros that require their own scope (in which case the
trailing semicolon is omitted from the macro definition and required to be provided by the user):

#define BAD_MACRO(x) f1(x); f2(x); f3(x);

// Only the call to f1 is protected by the condition here


if (cond) BAD_MACRO(var);

#define GOOD_MACRO(x) do { f1(x); f2(x); f3(x); } while(0)

// All calls are protected here


if (cond) GOOD_MACRO(var);

Section 15.5: Loop Control statements : Break and Continue


Loop control statements are used to change the flow of execution from its normal sequence. When execution
leaves a scope, all automatic objects that were created in that scope are destroyed. The break and continue are
loop control statements.

The break statement terminates a loop without any further consideration.

for (int i = 0; i < 10; i++)


{
if (i == 4)
break; // this will immediately exit our loop
std::cout << i << '\n';
}

The above code will print out:

1
2
3

The continue statement does not immediately exit the loop, but rather skips the rest of the loop body and goes to
the top of the loop (including checking the condition).

for (int i = 0; i < 6; i++)


{
if (i % 2 == 0) // evaluates to true if i is even
continue; // this will immediately go back to the start of the loop
/* the next line will only be reached if the above "continue" statement
does not execute */
std::cout << i << " is an odd number\n";
}

The above code will print out:

1 is an odd number
3 is an odd number
5 is an odd number

Because such control flow changes are sometimes difficult for humans to easily understand, break and continue
are used sparingly. More straightforward implementation are usually easier to read and understand. For example,

Complete C++ Tips & Secrets for Professionals 161


the first for loop with the break above might be rewritten as:

for (int i = 0; i < 4; i++)


{
std::cout << i << '\n';
}

The second example with continue might be rewritten as:

for (int i = 0; i < 6; i++)


{
if (i % 2 != 0) {
std::cout << i << " is an odd number\n";
}
}

Section 15.6: Declaration of variables in conditions


In the condition of the for and while loops, it's also permitted to declare an object. This object will be considered to
be in scope until the end of the loop, and will persist through each iteration of the loop:

for (int i = 0; i < 5; ++i) {


do_something(i);
}
// i is no longer in scope.

for (auto& a : some_container) {


a.do_something();
}
// a is no longer in scope.

while(std::shared_ptr<Object> p = get_object()) {
p->do_something();
}
// p is no longer in scope.

However, it is not permitted to do the same with a do...while loop; instead, declare the variable before the loop,
and (optionally) enclose both the variable and the loop within a local scope if you want the variable to go out of
scope after the loop ends:

//This doesn't compile


do {
s = do_something();
} while (short s > 0);

// Good
short s;
do {
s = do_something();
} while (s > 0);

This is because the statement portion of a do...while loop (the loop's body) is evaluated before the expression
portion (the while) is reached, and thus, any declaration in the expression will not be visible during the first iteration
of the loop.

Section 15.7: Range-for over a sub-range


Using range-base loops, you can loop over a sub-part of a given container or other range by generating a proxy
object that qualifies for range-based for loops.

Complete C++ Tips & Secrets for Professionals 162


template<class Iterator, class Sentinel=Iterator>
struct range_t {
Iterator b;
Sentinel e;
Iterator begin() const { return b; }
Sentinel end() const { return e; }
bool empty() const { return begin()==end(); }
range_t without_front( std::size_t count=1 ) const {
if (std::is_same< std::random_access_iterator_tag, typename
std::iterator_traits<Iterator>::iterator_category >{} ) {
count = (std::min)(std::size_t(std::distance(b,e)), count);
}
return {std::next(b, count), e};
}
range_t without_back( std::size_t count=1 ) const {
if (std::is_same< std::random_access_iterator_tag, typename
std::iterator_traits<Iterator>::iterator_category >{} ) {
count = (std::min)(std::size_t(std::distance(b,e)), count);
}
return {b, std::prev(e, count)};
}
};

template<class Iterator, class Sentinel>


range_t<Iterator, Sentinel> range( Iterator b, Sentinal e ) {
return {b,e};
}
template<class Iterable>
auto range( Iterable& r ) {
using std::begin; using std::end;
return range(begin(r),end(r));
}

template<class C>
auto except_first( C& c ) {
auto r = range(c);
if (r.empty()) return r;
return r.without_front();
}

now we can do:

std::vector<int> v = {1,2,3,4};

for (auto i : except_first(v))


std::cout << i << '\n';

and print out

2
3
4

Be aware that intermediate objects generated in the for(:range_expression) part of the for loop will have
expired by the time the for loop starts.

Chapter 16: std::map


Section 16.1: Accessing elements
An std::map takes (key, value) pairs as input.

Complete C++ Tips & Secrets for Professionals 163


Consider the following example of std::map initialization:

std::map < std::string, int > ranking { std::make_pair("stackoverflow", 2),


std::make_pair("docs-beta", 1) };

In an std::map , elements can be inserted as follows:

ranking["stackoverflow"]=2;
ranking["docs-beta"]=1;

In the above example, if the key stackoverflow is already present, its value will be updated to 2. If it isn't already
present, a new entry will be created.

In an std::map, elements can be accessed directly by giving the key as an index:

std::cout << ranking[ "stackoverflow" ] << std::endl;

Note that using the operator[] on the map will actually insert a new value with the queried key into the map. This
means that you cannot use it on a const std::map, even if the key is already stored in the map. To prevent this
insertion, check if the element exists (for example by using find()) or use at() as described below.

Version≥C++11

Elements of a std::map can be accessed with at():

std::cout << ranking.at("stackoverflow") << std::endl;

Note that at() will throw an std::out_of_range exception if the container does not contain the requested
element.

In both containers std::map and std::multimap, elements can be accessed using iterators:

Version≥C++11
// Example using begin()
std::multimap < int, std::string > mmp { std::make_pair(2, "stackoverflow"),
std::make_pair(1, "docs-beta"),
std::make_pair(2, "stackexchange") };
auto it = mmp.begin();
std::cout << it->first << " : " << it->second << std::endl; // Output: "1 : docs-beta"
it++;
std::cout << it->first << " : " << it->second << std::endl; // Output: "2 : stackoverflow"
it++;
std::cout << it->first << " : " << it->second << std::endl; // Output: "2 : stackexchange"

// Example using rbegin()


std::map < int, std::string > mp { std::make_pair(2, "stackoverflow"),
std::make_pair(1, "docs-beta"),
std::make_pair(2, "stackexchange") };
auto it2 = mp.rbegin();
std::cout << it2->first << " : " << it2->second << std::endl; // Output: "2 : stackoverflow"
it2++;
std::cout << it2->first << " : " << it2->second << std::endl; // Output: "1 : docs-beta"

Section 16.2: Inserting elements


An element can be inserted into a std::map only if its key is not already present in the map. Given for example:

std::map< std::string, size_t > fruits_count;

Complete C++ Tips & Secrets for Professionals 164


A key-value pair is inserted into a std::map through the insert() member function. It requires a pair as an
argument:

fruits_count.insert({"grapes", 20});
fruits_count.insert(make_pair("orange", 30));
fruits_count.insert(pair<std::string, size_t>("banana", 40));
fruits_count.insert(map<std::string, size_t>::value_type("cherry", 50));

The insert() function returns a pair consisting of an iterator and a bool value:

If the insertion was successful, the iterator points to the newly inserted element, and the bool value is
true.
If there was already an element with the same key, the insertion fails. When that happens, the iterator
points to the element causing the conflict, and the bool is value is false.

The following method can be used to combine insertion and searching operation:

auto success = fruits_count.insert({"grapes", 20});


if (!success.second) { // we already have 'grapes' in the map
success.first->second += 20; // access the iterator to update the value
}

For convenience, the std::map container provides the subscript operator to access elements in the map and
to insert new ones if they don't exist:

fruits_count["apple"] = 10;

While simpler, it prevents the user from checking if the element already exists. If an element is missing,
std::map::operator[] implicitly creates it, initializing it with the default constructor before overwriting it
with the supplied value.

insert() can be used to add several elements at once using a braced list of pairs. This version of insert()
returns void:

fruits_count.insert({{"apricot", 1}, {"jackfruit", 1}, {"lime", 1}, {"mango", 7}});

insert() can also be used to add elements by using iterators denoting the begin and end of value_type
values:

std::map< std::string, size_t > fruit_list{ {"lemon", 0}, {"olive", 0}, {"plum", 0}};
fruits_count.insert(fruit_list.begin(), fruit_list.end());

Example:

std::map<std::string, size_t> fruits_count;


std::string fruit;
while(std::cin >> fruit){
// insert an element with 'fruit' as key and '1' as value
// (if the key is already stored in fruits_count, insert does nothing)
auto ret = fruits_count.insert({fruit, 1});
if(!ret.second){ // 'fruit' is already in the map
++ret.first->second; // increment the counter
}
}

Time complexity for an insertion operation is O(log n) because std::map are implemented as trees.

Version≥C++11

Complete C++ Tips & Secrets for Professionals 165


A pair can be constructed explicitly using make_pair() and emplace():

std::map< std::string , int > runs;


runs.emplace("Babe Ruth", 714);
runs.insert(make_pair("Barry Bonds", 762));

If we know where the new element will be inserted, then we can use emplace_hint() to specify an iterator hint. If
the new element can be inserted just before hint, then the insertion can be done in constant time. Otherwise it
behaves in the same way as emplace():

std::map< std::string , int > runs;


auto it = runs.emplace("Barry Bonds", 762); // get iterator to the inserted element
// the next element will be before "Barry Bonds", so it is inserted before 'it'
runs.emplace_hint(it, "Babe Ruth", 714);

Section 16.3: Searching in std::map or in std::multimap


There are several ways to search a key in std::map or in std::multimap.

To get the iterator of the first occurrence of a key, the find() function can be used. It returns end() if the key
does not exist.

std::multimap< int , int > mmp{ {1, 2}, {3, 4}, {6, 5}, {8, 9}, {3, 4}, {6, 7} };
auto it = mmp.find(6);
if(it!=mmp.end())
std::cout << it->first << ", " << it->second << std::endl; //prints: 6, 5
else
std::cout << "Value does not exist!" << std::endl;

it = mmp.find(66);
if(it!=mmp.end())
std::cout << it->first << ", " << it->second << std::endl;
else
std::cout << "Value does not exist!" << std::endl; // This line would be executed.

Another way to find whether an entry exists in std::map or in std::multimap is using the count() function,
which counts how many values are associated with a given key. Since std::map associates only one value
with each key, its count() function can only return 0 (if the key is not present) or 1 (if it is). For
std::multimap, count() can return values greater than 1 since there can be several values associated with
the same key.

std::map< int , int > mp{ {1, 2}, {3, 4}, {6, 5}, {8, 9}, {3, 4}, {6, 7} };
if(mp.count(3) > 0) // 3 exists as a key in map
std::cout << "The key exists!" << std::endl; // This line would be executed.
else
std::cout << "The key does not exist!" << std::endl;

If you only care whether some element exists, find is strictly better: it documents your intent and, for
multimaps, it can stop once the first matching element has been found.

In the case of std::multimap, there could be several elements having the same key. To get this range, the
equal_range() function is used which returns std::pair having iterator lower bound (inclusive) and upper
bound (exclusive) respectively. If the key does not exist, both iterators would point to end().

auto eqr = mmp.equal_range(6);


auto st = eqr.first, en = eqr.second;
for(auto it = st; it != en; ++it){
std::cout << it->first << ", " << it->second << std::endl;

Complete C++ Tips & Secrets for Professionals 166


}
// prints: 6, 5
// 6, 7

Section 16.4: Initializing a std::map or std::multimap


std::map and std::multimap both can be initialized by providing key-value pairs separated by comma. Key-value
pairs could be provided by either {key, value} or can be explicitly created by std::make_pair(key, value). As
std::map does not allow duplicate keys and comma operator performs right to left, the pair on right would be
overwritten with the pair with same key on the left.

std::multimap < int, std::string > mmp { std::make_pair(2, "stackoverflow"),


std::make_pair(1, "docs-beta"),
std::make_pair(2, "stackexchange") };
// 1 docs-beta
// 2 stackoverflow
// 2 stackexchange

std::map < int, std::string > mp { std::make_pair(2, "stackoverflow"),


std::make_pair(1, "docs-beta"),
std::make_pair(2, "stackexchange") };
// 1 docs-beta
// 2 stackoverflow

Both could be initialized with iterator.

// From std::map or std::multimap iterator


std::multimap< int , int > mmp{ {1, 2}, {3, 4}, {6, 5}, {8, 9}, {6, 8}, {3, 4},
{6, 7} };
// {1, 2}, {3, 4}, {3, 4}, {6, 5}, {6, 8}, {6, 7}, {8, 9}
auto it = mmp.begin();
std::advance(it,3); //moved cursor on first {6, 5}
std::map< int, int > mp(it, mmp.end()); // {6, 5}, {8, 9}

//From std::pair array


std::pair< int, int > arr[10];
arr[0] = {1, 3};
arr[1] = {1, 5};
arr[2] = {2, 5};
arr[3] = {0, 1};
std::map< int, int > mp(arr,arr+4); //{0 , 1}, {1, 3}, {2, 5}

//From std::vector of std::pair


std::vector< std::pair<int, int> > v{ {1, 5}, {5, 1}, {3, 6}, {3, 2} };
std::multimap< int, int > mp(v.begin(), v.end());
// {1, 5}, {3, 6}, {3, 2}, {5, 1}

Section 16.5: Checking number of elements


The container std::map has a member function empty(), which returns true or false, depending on whether the
map is empty or not. The member function size() returns the number of element stored in a std::map container:

std::map<std::string , int> rank {{"facebook.com", 1} ,{"google.com", 2}, {"youtube.com", 3}};


if(!rank.empty()){
std::cout << "Number of elements in the rank map: " << rank.size() << std::endl;
}
else{
std::cout << "The rank map is empty" << std::endl;
}

Complete C++ Tips & Secrets for Professionals 167


Section 16.6: Types of Maps
Regular Map

A map is an associative container, containing key-value pairs.

#include <string>
#include <map>
std::map<std::string, size_t> fruits_count;

In the above example, std::string is the key type, and size_t is a value.

The key acts as an index in the map. Each key must be unique, and must be ordered.

If you need mutliple elements with the same key, consider using multimap (explained below)

If your value type does not specify any ordering, or you want to override the default ordering, you may
provide one:

#include <string>
#include <map>
#include <cstring>
struct StrLess {
bool operator()(const std::string& a, const std::string& b) {
return strncmp(a.c_str(), b.c_str(), 8)<0;
//compare only up to 8 first characters
}
}
std::map<std::string, size_t, StrLess> fruits_count2;

If StrLess comparator returns false for two keys, they are considered the same even if their actual contents
differ.

Multi-Map

Multimap allows multiple key-value pairs with the same key to be stored in the map. Otherwise, its interface and
creation is very similar to the regular map.

#include <string>
#include <map>
std::multimap<std::string, size_t> fruits_count;
std::multimap<std::string, size_t, StrLess> fruits_count2;
Hash-Map (Unordered Map)

A hash map stores key-value pairs similar to a regular map. It does not order the elements with respect to the key
though. Instead, a hash value for the key is used to quickly access the needed key-value pairs.

#include <string>
#include <unordered_map>
std::unordered_map<std::string, size_t> fruits_count;

Unordered maps are usually faster, but the elements are not stored in any predictable order. For example, iterating
over all elements in an unordered_map gives the elements in a seemingly random order.

Section 16.7: Deleting elements


Removing all elements:

Complete C++ Tips & Secrets for Professionals 168


std::multimap< int , int > mmp{ {1, 2}, {3, 4}, {6, 5}, {8, 9}, {3, 4}, {6, 7} };
mmp.clear(); //empty multimap

Removing element from somewhere with the help of iterator:

std::multimap< int , int > mmp{ {1, 2}, {3, 4}, {6, 5}, {8, 9}, {3, 4}, {6, 7} };
// {1, 2}, {3, 4}, {3, 4}, {6, 5}, {6, 7}, {8, 9}
auto it = mmp.begin();
std::advance(it,3); // moved cursor on first {6, 5}
mmp.erase(it); // {1, 2}, {3, 4}, {3, 4}, {6, 7}, {8, 9}

Removing all elements in a range:

std::multimap< int , int > mmp{ {1, 2}, {3, 4}, {6, 5}, {8, 9}, {3, 4}, {6, 7} };
// {1, 2}, {3, 4}, {3, 4}, {6, 5}, {6, 7}, {8, 9}
auto it = mmp.begin();
auto it2 = it;
it++; //moved first cursor on first {3, 4}
std::advance(it2,3); //moved second cursor on first {6, 5}
mmp.erase(it,it2); // {1, 2}, {6, 5}, {6, 7}, {8, 9}

Removing all elements having a provided value as key:

std::multimap< int , int > mmp{ {1, 2}, {3, 4}, {6, 5}, {8, 9}, {3, 4}, {6, 7} };
// {1, 2}, {3, 4}, {3, 4}, {6, 5}, {6, 7}, {8, 9}
mmp.erase(6); // {1, 2}, {3, 4}, {3, 4}, {8, 9}

Removing elements that satisfy a predicate pred:

std::map<int,int> m;
auto it = m.begin();
while (it != m.end())
{
if (pred(*it))
it = m.erase(it);
else
++it;
}

Section 16.8: Iterating over std::map or std::multimap


std::map or std::multimap could be traversed by the following ways:

std::multimap< int , int > mmp{ {1, 2}, {3, 4}, {6, 5}, {8, 9}, {3, 4}, {6, 7} };

//Range based loop - since C++11


for(const auto &x: mmp)
std::cout<< x.first <<":"<< x.second << std::endl;

//Forward iterator for loop: it would loop through first element to last element
//it will be a std::map< int, int >::iterator
for (auto it = mmp.begin(); it != mmp.end(); ++it)
std::cout<< it->first <<":"<< it->second << std::endl; //Do something with iterator

//Backward iterator for loop: it would loop through last element to first element
//it will be a std::map< int, int >::reverse_iterator
for (auto it = mmp.rbegin(); it != mmp.rend(); ++it)
std::cout<< it->first <<" "<< it->second << std::endl; //Do something with iterator

While iterating over a std::map or a std::multimap, the use of auto is preferred to avoid useless implicit
conversions (see this SO answer for more details).

Complete C++ Tips & Secrets for Professionals 169


Section 16.9: Creating std::map with user-defined types as key
In order to be able to use a class as the key in a map, all that is required of the key is that it be copiable and
assignable. The ordering within the map is defined by the third argument to the template (and the argument to
the constructor, if used). This defaults to std::less<KeyType>, which defaults to the < operator, but there's no
requirement to use the defaults. Just write a comparison operator (preferably as a functional object):

struct CmpMyType
{
bool operator()( MyType const& lhs, MyType const& rhs ) const
{
// ...
}
};

In C++, the "compare" predicate must be a strict weak ordering. In particular, compare(X,X) must return false for
any X. i.e. if CmpMyType()(a, b) returns true, then CmpMyType()(b, a) must return false, and if both return false,
the elements are considered equal (members of the same equivalence class).

Strict Weak Ordering

This is a mathematical term to define a relationship between two objects.


Its definition is:

Two objects x and y are equivalent if both f(x, y) and f(y, x) are false. Note that an object is always (by the irreflexivity
invariant) equivalent to itself.

In terms of C++ this means if you have two objects of a given type, you should return the following values when
compared with the operator <.

X a;
X b;

Condition: Test: Result


a is equivalent to b: a < b false
a is equivalent to b b < a false

a is less than b a < b true


a is less than b b < a false

b is less than a a < b false


b is less than a b < a true

How you define equivalent/less is totally dependent on the type of your object.

Chapter 17: Threading


thread()
thread(thread&& other)
explicit thread(Function&& func, Args&&... args)

Parameter Details
other Takes ownership of other, other doesn't own the thread anymore
func Function to call in a seperate thread
args Arguments for func

Complete C++ Tips & Secrets for Professionals 170


Section 17.1: Creating a std::thread
In C++, threads are created using the std::thread class. A thread is a separate flow of execution; it is analogous to
having a helper perform one task while you simultaneously perform another. When all the code in the thread is
executed, it terminates. When creating a thread, you need to pass something to be executed on it. A few things that
you can pass to a thread:

Free functions
Member functions
Functor objects
Lambda expressions

Free function example - executes a function on a separate thread (Live Example):

#include <iostream>
#include <thread>

void foo(int a)
{
std::cout << a << '\n';
}

int main()
{
// Create and execute the thread
std::thread thread(foo, 10); // foo is the function to execute, 10 is the
// argument to pass to it

// Keep going; the thread is executed separately

// Wait for the thread to finish; we stay here until it is done


thread.join();

return 0;
}

Member function example - executes a member function on a separate thread (Live Example):

#include <iostream>
#include <thread>

class Bar
{
public:
void foo(int a)
{
std::cout << a << '\n';
}
};

int main()
{
Bar bar;

// Create and execute the thread


std::thread thread(&Bar::foo, &bar, 10); // Pass 10 to member function

// The member function will be executed in a separate thread

// Wait for the thread to finish, this is a blocking operation

Complete C++ Tips & Secrets for Professionals 171


thread.join();

return 0;
}

Functor object example (Live Example):

#include <iostream>
#include <thread>

class Bar
{
public:
void operator()(int a)
{
std::cout << a << '\n';
}
};

int main()
{
Bar bar;

// Create and execute the thread


std::thread thread(bar, 10); // Pass 10 to functor object

// The functor object will be executed in a separate thread

// Wait for the thread to finish, this is a blocking operation


thread.join();

return 0;
}

Lambda expression example (Live Example):

#include <iostream>
#include <thread>

int main()
{
auto lambda = [](int a) { std::cout << a << '\n'; };

// Create and execute the thread


std::thread thread(lambda, 10); // Pass 10 to the lambda expression

// The lambda expression will be executed in a separate thread

// Wait for the thread to finish, this is a blocking operation


thread.join();

return 0;
}

Section 17.2: Passing a reference to a thread


You cannot pass a reference (or const reference) directly to a thread because std::thread will copy/move them.
Instead, use std::reference_wrapper:

void foo(int& b)
{
b = 10;

Complete C++ Tips & Secrets for Professionals 172


}

int a = 1;
std::thread thread{ foo, std::ref(a) }; //'a' is now really passed as reference

thread.join();
std::cout << a << '\n'; //Outputs 10
void bar(const ComplexObject& co)
{
co.doCalculations();
}

ComplexObject object;
std::thread thread{ bar, std::cref(object) }; //'object' is passed as const&

thread.join();
std::cout << object.getResult() << '\n'; //Outputs the result

Section 17.3: Using std::async instead of std::thread


std::async is also able to make threads. Compared to std::thread it is considered less powerful but easier to use
when you just want to run a function asynchronously.

Asynchronously calling a function


#include <future>
#include <iostream>

unsigned int square(unsigned int i){


return i*i;
}

int main() {
auto f = std::async(std::launch::async, square, 8);
std::cout << "square currently running\n"; //do something while square is running
std::cout << "result is " << f.get() << '\n'; //getting the result from square
}
Common Pitfalls

std::async returns a std::future that holds the return value that will be calculated by the function. When
that future gets destroyed it waits until the thread completes, making your code effectively single threaded.
This is easily overlooked when you don't need the return value:

std::async(std::launch::async, square, 5);


//thread already completed at this point, because the returning future got destroyed

std::async works without a launch policy, so std::async(square, 5); compiles. When you do that the
system gets to decide if it wants to create a thread or not. The idea was that the system chooses to make a
thread unless it is already running more threads than it can run efficiently. Unfortunately implementations
commonly just choose not to create a thread in that situation, ever, so you need to override that behavior
with std::launch::async which forces the system to create a thread.

Beware of race conditions.

More on async on Futures and Promises

Complete C++ Tips & Secrets for Professionals 173


Section 17.4: Basic Synchronization
Thread synchronization can be accomplished using mutexes, among other synchronization primitives. There are
several mutex types provided by the standard library, but the simplest is std::mutex. To lock a mutex, you
construct a lock on it. The simplest lock type is std::lock_guard:

std::mutex m;
void worker() {
std::lock_guard<std::mutex> guard(m); // Acquires a lock on the mutex
// Synchronized code here
} // the mutex is automatically released when guard goes out of scope

With std::lock_guard the mutex is locked for the whole lifetime of the lock object. In cases where you need to
manually control the regions for locking, use std::unique_lock instead:

std::mutex m;
void worker() {
// by default, constructing a unique_lock from a mutex will lock the mutex
// by passing the std::defer_lock as a second argument, we
// can construct the guard in an unlocked state instead and
// manually lock later.
std::unique_lock<std::mutex> guard(m, std::defer_lock);
// the mutex is not locked yet!
guard.lock();
// critical section
guard.unlock();
// mutex is again released
}

More Thread synchronization structures

Section 17.5: Create a simple thread pool


C++11 threading primitives are still relatively low level. They can be used to write a higher level construct, like a
thread pool:

Version≥C++14
struct tasks {
// the mutex, condition variable and deque form a single
// thread-safe triggered queue of tasks:
std::mutex m;
std::condition_variable v;
// note that a packaged_task<void> can store a packaged_task<R>:
std::deque<std::packaged_task<void()>> work;

// this holds futures representing the worker threads being done:


std::vector<std::future<void>> finished;

// queue( lambda ) will enqueue the lambda into the tasks for the threads
// to use. A future of the type the lambda returns is given to let you get
// the result out.
template<class F, class R=std::result_of_t<F&()>>
std::future<R> queue(F&& f) {
// wrap the function object into a packaged task, splitting
// execution from the return value:
std::packaged_task<R()> p(std::forward<F>(f));

auto r=p.get_future(); // get the return value before we hand off the task
{
std::unique_lock<std::mutex> l(m);
work.emplace_back(std::move(p)); // store the task<R()> as a task<void()>

Complete C++ Tips & Secrets for Professionals 174


}
v.notify_one(); // wake a thread to work on the task

return r; // return the future result of the task


}

// start N threads in the thread pool.


void start(std::size_t N=1){
for (std::size_t i = 0; i < N; ++i)
{
// each thread is a std::async running this->thread_task():
finished.push_back(
std::async(
std::launch::async,
[this]{ thread_task(); }
)
);
}
}
// abort() cancels all non-started tasks, and tells every working thread
// stop running, and waits for them to finish up.
void abort() {
cancel_pending();
finish();
}
// cancel_pending() merely cancels all non-started tasks:
void cancel_pending() {
std::unique_lock<std::mutex> l(m);
work.clear();
}
// finish enques a "stop the thread" message for every thread, then waits for them:
void finish() {
{
std::unique_lock<std::mutex> l(m);
for(auto&&unused:finished){
work.push_back({});
}
}
v.notify_all();
finished.clear();
}
~tasks() {
finish();
}
private:
// the work that a worker thread does:
void thread_task() {
while(true){
// pop a task off the queue:
std::packaged_task<void()> f;
{
// usual thread-safe queue code:
std::unique_lock<std::mutex> l(m);
if (work.empty()){
v.wait(l,[&]{return !work.empty();});
}
f = std::move(work.front());
work.pop_front();
}
// if the task is invalid, it means we are asked to abort:
if (!f.valid()) return;
// otherwise, run the task:

Complete C++ Tips & Secrets for Professionals 175


f();
}
}
};

tasks.queue( []{ return "hello world"s; } ) returns a std::future<std::string>, which when the tasks
object gets around to running it is populated with hello world.

You create threads by running tasks.start(10) (which starts 10 threads).

The use of packaged_task<void()> is merely because there is no type-erased std::function equivalent that stores
move-only types. Writing a custom one of those would probably be faster than using packaged_task<void()>.

Live example.

Version=C++11

In C++11, replace result_of_t<blah> with typename result_of<blah>::type.

More on Mutexes.

Section 17.6: Ensuring a thread is always joined


When the destructor for std::thread is invoked, a call to either join() or detach() must have been made. If a
thread has not been joined or detached, then by default std::terminate will be called. Using RAII, this is generally
simple enough to accomplish:

class thread_joiner
{
public:

thread_joiner(std::thread t)
: t_(std::move(t))
{ }

~thread_joiner()
{
if(t_.joinable()) {
t_.join();
}
}

private:

std::thread t_;
}

This is then used like so:

void perform_work()
{
// Perform some work
}

void t()
{
thread_joiner j{std::thread(perform_work)};
// Do some other calculations while thread is running
} // Thread is automatically joined here

Complete C++ Tips & Secrets for Professionals 176


This also provides exception safety; if we had created our thread normally and the work done in t() performing
other calculations had thrown an exception, join() would never have been called on our thread and our process
would have been terminated.

Section 17.7: Operations on the current thread


std::this_thread is a namespace which has functions to do interesting things on the current thread from function
it is called from.

Function Description
get_id Returns the id of the thread
sleep_for Sleeps for a specified amount of time
sleep_until Sleeps until a specific time
yield Reschedule running threads, giving other threads priority

Getting the current threads id using std::this_thread::get_id:

void foo()
{
//Print this threads id
std::cout << std::this_thread::get_id() << '\n';
}

std::thread thread{ foo };


thread.join(); //'threads' id has now been printed, should be something like 12556

foo(); //The id of the main thread is printed, should be something like 2420

Sleeping for 3 seconds using std::this_thread::sleep_for:

void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(3));
}

std::thread thread{ foo };


foo.join();

std::cout << "Waited for 3 seconds!\n";

Sleeping until 3 hours in the future using std::this_thread::sleep_until:

void foo()
{
std::this_thread::sleep_until(std::chrono::system_clock::now() + std::chrono::hours(3));
}

std::thread thread{ foo };


thread.join();

std::cout << "We are now located 3 hours after the thread has been called\n";

Letting other threads take priority using std::this_thread::yield:

void foo(int a)
{
for (int i = 0; i < al ++i)
std::this_thread::yield(); //Now other threads take priority, because this thread
//isn't doing anything important

Complete C++ Tips & Secrets for Professionals 177


std::cout << "Hello World!\n";
}

std::thread thread{ foo, 10 };


thread.join();

Section 17.8: Using Condition Variables


A condition variable is a primitive used in conjunction with a mutex to orchestrate communication between
threads. While it is neither the exclusive or most efficient way to accomplish this, it can be among the simplest to
those familiar with the pattern.

One waits on a std::condition_variable with a std::unique_lock<std::mutex>. This allows the code to safely
examine shared state before deciding whether or not to proceed with acquisition.

Below is a producer-consumer sketch that uses std::thread, std::condition_variable, std::mutex, and a few
others to make things interesting.

#include <condition_variable>
#include <cstddef>
#include <iostream>
#include <mutex>
#include <queue>
#include <random>
#include <thread>

int main()
{
std::condition_variable cond;
std::mutex mtx;
std::queue<int> intq;
bool stopped = false;

std::thread producer{[&]()
{
// Prepare a random number generator.
// Our producer will simply push random numbers to intq.
//
std::default_random_engine gen{};
std::uniform_int_distribution<int> dist{};

std::size_t count = 4006;


while(count--)
{
// Always lock before changing
// state guarded by a mutex and
// condition_variable (a.k.a. "condvar").
std::lock_guard<std::mutex> L{mtx};

// Push a random int into the queue


intq.push(dist(gen));

// Tell the consumer it has an int


cond.notify_one();
}

// All done.
// Acquire the lock, set the stopped flag,
// then inform the consumer.
std::lock_guard<std::mutex> L{mtx};

Complete C++ Tips & Secrets for Professionals 178


std::cout << "Producer is done!" << std::endl;

stopped = true;
cond.notify_one();
}};

std::thread consumer{[&]()
{
do{
std::unique_lock<std::mutex> L{mtx};
cond.wait(L,[&]()
{
// Acquire the lock only if
// we've stopped or the queue
// isn't empty
return stopped || ! intq.empty();
});

// We own the mutex here; pop the queue


// until it empties out.

while( ! intq.empty())
{
const auto val = intq.front();
intq.pop();

std::cout << "Consumer popped: " << val << std::endl;


}

if(stopped){
// producer has signaled a stop
std::cout << "Consumer is done!" << std::endl;
break;
}

}while(true);
}};

consumer.join();
producer.join();

std::cout << "Example Completed!" << std::endl;

return 0;
}

Section 17.9: Thread operations


When you start a thread, it will execute until it is finished.

Often, at some point, you need to (possibly - the thread may already be done) wait for the thread to finish, because
you want to use the result for example.

int n;
std::thread thread{ calculateSomething, std::ref(n) };

//Doing some other stuff

//We need 'n' now!


//Wait for the thread to finish - if it is not already done
thread.join();

Complete C++ Tips & Secrets for Professionals 179


//Now 'n' has the result of the calculation done in the seperate thread
std::cout << n << '\n';

You can also detach the thread, letting it execute freely:

std::thread thread{ doSomething };

//Detaching the thread, we don't need it anymore (for whatever reason)


thread.detach();

//The thread will terminate when it is done, or when the main thread returns

Section 17.10: Thread-local storage


Thread-local storage can be created using the thread_local keyword. A variable declared with the thread_local
specifier is said to have thread storage duration.

Each thread in a program has its own copy of each thread-local variable.
A thread-local variable with function (local) scope will be initialized the first time control passes through its
definition. Such a variable is implicitly static, unless declared extern.
A thread-local variable with namespace or class (non-local) scope will be initialized as part of thread startup.
Thread-local variables are destroyed upon thread termination.
A member of a class can only be thread-local if it is static. There will therefore be one copy of that variable
per thread, rather than one copy per (thread, instance) pair.

Example:

void debug_counter() {
thread_local int count = 0;
Logger::log("This function has been called %d times by this thread", ++count);
}

Section 17.11: Reassigning thread objects


We can create empty thread objects and assign work to them later.

If we assign a thread object to another active, joinable thread, std::terminate will automatically be called before
the thread is replaced.

#include <thread>

void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(3));
}
//create 100 thread objects that do nothing
std::thread executors[100];

// Some code

// I want to create some threads now

for (int i = 0;i < 100;i++)


{
// If this object doesn't have a thread assigned
if (!executors[i].joinable())
executors[i] = std::thread(foo);
}

Complete C++ Tips & Secrets for Professionals 180


Chapter 18: Value Categories
Section 18.1: Value Category Meanings
Expressions in C++ are assigned a particular value category, based on the result of those expressions. Value
categories for expressions can affect C++ function overload resolution.

Value categories determines two important-but-separate properties about an expression. One property is whether
the expression has identity. An expression has identity if it refers to an object that has a variable name. The variable
name may not be involved in the expression, but the object can still have one.

The other property is whether it is legal to implicitly move from the expression's value. Or more specifically,
whether the expression, when used as a function parameter, will bind to r-value parameter types or not.

C++ defines 3 value categories which represent the useful combination of these properties: lvalue (expressions with
identity but not movable from), xvalue (expressions with identity that are moveable from), and prvalue (expressions
without identity that are moveable from). C++ does not have expressions which have no identity and cannot be
moved from.

C++ defines two other value categories, each based solely on one of these properties: glvalue (expressions with
identity) and rvalue (expressions that can be moved from). These act as useful groupings of the prior categories.

This graph serves as an illustration:

Section 18.2: rvalue


An rvalue expression is any expression which can be implicitly moved from, regardless of whether it has identity.

More precisely, rvalue expressions may be used as the argument to a function that takes a parameter of type T &&
(where T is the type of expr). Only rvalue expressions may be given as arguments to such function parameters; if a
non-rvalue expression is used, then overload resolution will pick any function that does not use an rvalue reference
parameter. And if none exist, then you get an error.

The category of rvalue expressions includes all xvalue and prvalue expressions, and only those expressions.

The standard library function std::move exists to explicitly transform a non-rvalue expression into an rvalue. More
specifically, it turns the expression into an xvalue, since even if it was an identity-less prvalue expression before, by
passing it as a parameter to std::move, it gains identity (the function's parameter name) and becomes an xvalue.

Consider the following:

Complete C++ Tips & Secrets for Professionals 181


std::string str("init"); //1
std::string test1(str); //2
std::string test2(std::move(str)); //3

str = std::string("new value"); //4


std::string &&str_ref = std::move(str); //5
std::string test3(str_ref); //6

std::string has a constructor which takes a single parameter of type std::string&&, commonly called a "move
constructor". However, the value category of the expression str is not an rvalue (specifically it is an lvalue), so it
cannot call that constructor overload. Instead, it calls the const std::string& overload, the copy constructor.

Line 3 changes things. The return value of std::move is a T&&, where T is the base type of the parameter passed in.
So std::move(str) returns std::string&&. A function call who's return value is an rvalue reference is an rvalue
expression (specifically an xvalue), so it may call the move constructor of std::string. After line 3, str has been
moved from (who's contents are now undefined).

Line 4 passes a temporary to the assignment operator of std::string. This has an overload which takes a
std::string&&. The expression std::string("new value") is an rvalue expression (specifically a prvalue), so it
may call that overload. Thus, the temporary is moved into str, replacing the undefined contents with specific
contents.

Line 5 creates a named rvalue reference called str_ref that refers to str. This is where value categories get
confusing.

See, while str_ref is an rvalue reference to std::string, the value category of the expression str_ref is not an
rvalue. It is an lvalue expression. Yes, really. Because of this, one cannot call the move constructor of std::string
with the expression str_ref. Line 6 therefore copies the value of str into test3.

To move it, we would have to employ std::move again.

Section 18.3: xvalue


An xvalue (eXpiring value) expression is an expression which has identity and represents an object which can be
implicitly moved from. The general idea with xvalue expressions is that the object they represent is going to be
destroyed soon (hence the "eXpiring" part), and therefore implicitly moving from them is fine.

Given:

struct X { int n; };
extern X x;

4; // prvalue: does not have an identity


x; // lvalue
x.n; // lvalue
std::move(x); // xvalue
std::forward<X&>(x); // lvalue
X{4}; // prvalue: does not have an identity
X{4}.n; // xvalue: does have an identity and denotes resources
// that can be reused

Section 18.4: prvalue


A prvalue (pure-rvalue) expression is an expression which lacks identity, whose evaluation is typically used to
initialize an object, and which can be implicitly moved from. These include, but are not limited to:

Expressions that represent temporary objects, such as std::string("123").


A function call expression that does not return a reference

Complete C++ Tips & Secrets for Professionals 182


A literal (except a string literal - those are lvalues), such has 1, true, 0.5f, or 'a'
A lambda expression

The built-in addressof operator (&) cannot be applied on these expressions.

Section 18.5: lvalue


An lvalue expression is an expression which has identity, but cannot be implicitly moved from. Among these are
expressions that consist of a variable name, function name, expressions that are built-in dereference operator uses
and expressions that refer to lvalue references.

The typical lvalue is simply a name, but lvalues can come in other flavors as well:

struct X { ... };

X x; // x is an lvalue
X* px = &x; // px is an lvalue
*px = X{}; // *px is also an lvalue, X{} is a prvalue

X* foo_ptr(); // foo_ptr() is a prvalue


X& foo_ref(); // foo_ref() is an lvalue

Additionally, while most literals (e.g. 4, 'x', etc.) are prvalues, string literals are lvalues.

Section 18.6: glvalue


A glvalue (a "generalized lvalue") expression is any expression which has identity, regardless of whether it can be
moved from or not. This category includes lvalues (expressions that have identity but can't be moved from) and
xvalues (expressions that have identity, and can be moved from), but excludes prvalues (expressions without
identity).

If an expression has a name, it's a glvalue:

struct X { int n; };
X foo();

X x;
x; // has a name, so it's a glvalue
std::move(x); // has a name (we're moving from "x"), so it's a glvalue
// can be moved from, so it's an xvalue not an lvalue

foo(); // has no name, so is a prvalue, not a glvalue


X{}; // temporary has no name, so is a prvalue, not a glvalue
X{}.n; // HAS a name, so is a glvalue. can be moved from, so it's an xvalue

Chapter 19: Preprocessor


The C preprocessor is a simple text parser/replacer that is run before the actual compilation of the code. Used to
extend and ease the use of the C (and later C++) language, it can be used for:

a. Including other files using #include

b. Define a text-replacement macro using #define

c. Conditional Compilation using#if #ifdef

d. Platform/Compiler specific logic (as an extension of conditional compilation)

Complete C++ Tips & Secrets for Professionals 183


Section 19.1: Include Guards
A header file may be included by other header files. A source file (compilation unit) that includes multiple headers
may therefore, indirectly, include some headers more than once. If such a header file that is included more than
once contains definitions, the compiler (after preprocessing) detects a violation of the One Definition Rule (e.g. §3.2
of the 2003 C++ standard) and therefore issues a diagnostic and compilation fails.

Multiple inclusion is prevented using "include guards", which are sometimes also known as header guards or macro
guards. These are implemented using the preprocessor #define, #ifndef, #endif directives.

e.g.

// Foo.h
#ifndef FOO_H_INCLUDED
#define FOO_H_INCLUDED

class Foo // a class definition


{
};

#endif

The key advantage of using include guards is that they will work with all standard-compliant compilers and
preprocessors.

However, include guards also cause some problems for developers, as it is necessary to ensure the macros are
unique within all headers used in a project. Specifically, if two (or more) headers use FOO_H_INCLUDED as their
include guard, the first of those headers included in a compilation unit will effectively prevent the others from being
included. Particular challenges are introduced if a project uses a number of third-party libraries with header files
that happen to use include guards in common.

It is also necessary to ensure that the macros used in include guards do not conflict with any other macros defined
in header files.

Most C++ implementations also support the #pragma once directive which ensures the file is only included once
within a single compilation. This is a de facto standard directive, but it is not part of any ISO C++ standard. For
example:

// Foo.h
#pragma once

class Foo
{
};

While #pragma once avoids some problems associated with include guards, a #pragma - by definition in the
standards - is inherently a compiler-specific hook, and will be silently ignored by compilers that don't support it.
Projects which use #pragma once are more difficult to port to compilers that don't support it.

A number of coding guidelines and assurance standards for C++ specifically discourage any use of the preprocessor
other than to #include header files or for the purposes of placing include guards in headers.

Section 19.2: Conditional logic and cross-platform handling


In a nutshell, conditional pre-processing logic is about making code-logic available or unavailable for compilation
using macro definitions.

Complete C++ Tips & Secrets for Professionals 184


Three prominent use-cases are:

different app profiles (e.g. debug, release, testing, optimised) that can be candidates of the same app (e.g.
with extra logging).
cross-platform compiles - single code-base, multiple compilation platforms.
utilising a common code-base for multiple application versions (e.g. Basic, Premium and Pro versions of a
software) - with slightly different features.

Example a: A cross-platform approach for removing files (illustrative):

#ifdef _WIN32
#include <windows.h> // and other windows system files
#endif
#include <cstdio>

bool remove_file(const std::string &path)


{
#ifdef _WIN32
return DeleteFile(path.c_str());
#elif defined(_POSIX_VERSION) || defined(__unix__)
return (0 == remove(path.c_str()));
#elif defined(__APPLE__)
//TODO: check if NSAPI has a more specific function with permission dialog
return (0 == remove(path.c_str()));
#else
#error "This platform is not supported"
#endif
}

Macros like _WIN32, __APPLE__ or __unix__ are normally predefined by corresponding implementations.

Example b: Enabling additional logging for a debug build:

void s_PrintAppStateOnUserPrompt()
{
std::cout << "--------BEGIN-DUMP---------------\n"
<< AppState::Instance()->Settings().ToString() << "\n"
#if ( 1 == TESTING_MODE ) //privacy: we want user details only when testing
<< ListToString(AppState::UndoStack()->GetActionNames())
<< AppState::Instance()->CrntDocument().Name()
<< AppState::Instance()->CrntDocument().SignatureSHA() << "\n"
#endif
<< "--------END-DUMP---------------\n"
}

Example c: Enable a premium feature in a separate product build (note: this is illustrative. it is often a better idea to
allow a feature to be unlocked without the need to reinstall an application)

void MainWindow::OnProcessButtonClick()
{
#ifndef _PREMIUM
CreatePurchaseDialog("Buy App Premium", "This feature is available for our App Premium users.
Click the Buy button to purchase the Premium version at our website");
return;
#endif
//...actual feature logic here
}

Some common tricks:

Complete C++ Tips & Secrets for Professionals 185


Defining symbols at invocation time:

The preprocessor can be called with predefined symbols (with optional initialisation). For example this command
(gcc -E runs only the preprocessor)

gcc -E -DOPTIMISE_FOR_OS_X -DTESTING_MODE=1 Sample.cpp

processes Sample.cpp in the same way as it would if #define OPTIMISE_FOR_OS_X and #define TESTING_MODE 1
were added to the top of Sample.cpp.

Ensuring a macro is defined:

If a macro isn't defined and its value is compared or checked, the preprocessor almost always silently assumes the
value to be 0. There are a few ways to work with this. One approach is to assume that the default settings are
represented as 0, and any changes (e.g. to the app build profile) needs to be explicitly done (e.g.
ENABLE_EXTRA_DEBUGGING=0 by default, set -DENABLE_EXTRA_DEBUGGING=1 to override). Another approach is
make all definitions and defaults explicit. This can be achieved using a combination of #ifndef and #error
directives:

#ifndef (ENABLE_EXTRA_DEBUGGING)
// please include DefaultDefines.h if not already included.
# error "ENABLE_EXTRA_DEBUGGING is not defined"
#else
# if ( 1 == ENABLE_EXTRA_DEBUGGING )
//code
# endif
#endif

Section 19.3: X-macros


An idiomatic technique for generating repeating code structures at compile time.

An X-macro consists of two parts: the list, and the execution of the list.

Example:

#define LIST \
X(dog) \
X(cat) \
X(racoon)

// class Animal {
// public:
// void say();
// };

#define X(name) Animal name;


LIST
#undef X

int main() {
#define X(name) name.say();
LIST
#undef X

return 0;
}

which is expanded by the preprocessor into the following:

Complete C++ Tips & Secrets for Professionals 186


Animal dog;
Animal cat;
Animal racoon;

int main() {
dog.say();
cat.say();
racoon.say();

return 0;
}

As lists become bigger (let's say, more than 100 elements), this technique helps to avoid excessive copy-pasting.

Source: https://en.wikipedia.org/wiki/X_Macro

See also: X-macros

If defining a seamingly irrelevant X before using LIST is not to your liking, you can pass a macro name as an
argument as well:

#define LIST(MACRO) \
MACRO(dog) \
MACRO(cat) \
MACRO(racoon)

Now, you explicitly specify which macro should be used when expanding the list, e.g.

#define FORWARD_DECLARE_ANIMAL(name) Animal name;


LIST(FORWARD_DECLARE_ANIMAL)

If each invocation of the MACRO should take additional parameters - constant with respect to the list, variadic macros
can be used

//a walkaround for Visual studio


#define EXPAND(x) x

#define LIST(MACRO, ...) \


EXPAND(MACRO(dog, __VA_ARGS__)) \
EXPAND(MACRO(cat, __VA_ARGS__)) \
EXPAND(MACRO(racoon, __VA_ARGS__))

The first argument is supplied by the LIST, while the rest is provided by the user in the LIST invocation. For
example:

#define FORWARD_DECLARE(name, type, prefix) type prefix##name;


LIST(FORWARD_DECLARE,Animal,anim_)
LIST(FORWARD_DECLARE,Object,obj_)

will expand to

Animal anim_dog;
Animal anim_cat;
Animal anim_racoon;
Object obj_dog;
Object obj_cat;
Object obj_racoon;

Section 19.4: Macros


Macros are categorized into two main groups: object-like macros and function-like macros. Macros are treated as a

Complete C++ Tips & Secrets for Professionals 187


token substitution early in the compilation process. This means that large (or repeating) sections of code can be
abstracted into a preprocessor macro.

// This is an object-like macro


#define PI 3.14159265358979

// This is a function-like macro.


// Note that we can use previously defined macros
// in other macro definitions (object-like or function-like)
// But watch out, its quite useful if you know what you're doing, but the
// Compiler doesnt know which type to handle, so using inline functions instead
// is quite recommended (But e.g. for Minimum/Maximum functions it is quite useful)
#define AREA(r) (PI*(r)*(r))

// They can be used like this:


double pi_macro = PI;
double area_macro = AREA(4.6);

The Qt library makes use of this technique to create a meta-object system by having the user declare the Q_OBJECT
macro at the head of the user-defined class extending QObject.

Macro names are usually written in all caps, to make them easier to differentiate from normal code. This isn't a
requirement, but is merely considered good style by many programmers.

When an object-like macro is encountered, it's expanded as a simple copy-paste operation, with the macro's name
being replaced with its definition. When a function-like macro is encountered, both its name and its parameters are
expanded.

double pi_squared = PI * PI;


// Compiler sees:
double pi_squared = 3.14159265358979 * 3.14159265358979;

double area = AREA(5);


// Compiler sees:
double area = (3.14159265358979*(5)*(5))

Due to this, function-like macro parameters are often enclosed within parentheses, as in AREA() above. This is to
prevent any bugs that can occur during macro expansion, specifically bugs caused by a single macro parameter
being composed of multiple actual values.

#define BAD_AREA(r) PI * r * r

double bad_area = BAD_AREA(5 + 1.6);


// Compiler sees:
double bad_area = 3.14159265358979 * 5 + 1.6 * 5 + 1.6;

double good_area = AREA(5 + 1.6);


// Compiler sees:
double good_area = (3.14159265358979*(5 + 1.6)*(5 + 1.6));

Also note that due to this simple expansion, care must be taken with the parameters passed to macros, to prevent
unexpected side effects. If the parameter is modified during evaluation, it will be modified each time it is used in
the expanded macro, which usually isn't what we want. This is true even if the macro encloses the parameters in
parentheses to prevent expansion from breaking anything.

int oops = 5;
double incremental_damage = AREA(oops++);
// Compiler sees:
double incremental_damage = (3.14159265358979*(oops++)*(oops++));

Complete C++ Tips & Secrets for Professionals 188


Additionally, macros provide no type-safety, leading to hard-to-understand errors about type mismatch.

As programmers normally terminate lines with a semicolon, macros that are intended to be used as standalone
lines are often designed to "swallow" a semicolon; this prevents any unintended bugs from being caused by an
extra semicolon.

#define IF_BREAKER(Func) Func();

if (some_condition)
// Oops.
IF_BREAKER(some_func);
else
std::cout << "I am accidentally an orphan." << std::endl;

In this example, the inadvertent double semicolon breaks the if...else block, preventing the compiler from
matching the else to the if. To prevent this, the semicolon is omitted from the macro definition, which will cause it
to "swallow" the semicolon immediately following any usage of it.

#define IF_FIXER(Func) Func()

if (some_condition)
IF_FIXER(some_func);
else
std::cout << "Hooray! I work again!" << std::endl;

Leaving off the trailing semicolon also allows the macro to be used without ending the current statement, which
can be beneficial.

#define DO_SOMETHING(Func, Param) Func(Param, 2)

// ...

some_function(DO_SOMETHING(some_func, 3), DO_SOMETHING(some_func, 42));

Normally, a macro definition ends at the end of the line. If a macro needs to cover multiple lines, however, a
backslash can be used at the end of a line to indicate this. This backslash must be the last character in the line,
which indicates to the preprocessor that the following line should be concatenated onto the current line, treating
them as a single line. This can be used multiple times in a row.

#define TEXT "I \


am \
many \
lines."

// ...

std::cout << TEXT << std::endl; // Output: I am many lines.

This is especially useful in complex function-like macros, which may need to cover multiple lines.

#define CREATE_OUTPUT_AND_DELETE(Str) \
std::string* tmp = new std::string(Str); \
std::cout << *tmp << std::endl; \
delete tmp;

// ...

CREATE_OUTPUT_AND_DELETE("There's no real need for this to use 'new'.")

In the case of more complex function-like macros, it can be useful to give them their own scope to prevent possible

Complete C++ Tips & Secrets for Professionals 189


name collisions or to cause objects to be destroyed at the end of the macro, similar to an actual function. A
common idiom for this is do while 0, where the macro is enclosed in a do-while block. This block is generally not
followed with a semicolon, allowing it to swallow a semicolon.

#define DO_STUFF(Type, Param, ReturnVar) do { \


Type temp(some_setup_values); \
ReturnVar = temp.process(Param); \
} while (0)

int x;
DO_STUFF(MyClass, 41153.7, x);

// Compiler sees:

int x;
do {
MyClass temp(some_setup_values);
x = temp.process(41153.7);
} while (0);

There are also variadic macros; similarly to variadic functions, these take a variable number of arguments, and then
expand them all in place of a special "Varargs" parameter, __VA_ARGS__.

#define VARIADIC(Param, ...) Param(__VA_ARGS__)

VARIADIC(printf, "%d", 8);


// Compiler sees:
printf("%d", 8);

Note that during expansion, __VA_ARGS__ can be placed anywhere in the definition, and will be expanded correctly.

#define VARIADIC2(POne, PTwo, PThree, ...) POne(PThree, __VA_ARGS__, PTwo)

VARIADIC2(some_func, 3, 8, 6, 9);
// Compiler sees:
some_func(8, 6, 9, 3);

In the case of a zero-argument variadic parameter, different compilers will handle the trailing comma differently.
Some compilers, such as Visual Studio, will silently swallow the comma without any special syntax. Other compilers,
such as GCC, require you to place ## immediately before __VA_ARGS__. Due to this, it is wise to conditionally define
variadic macros when portability is a concern.

// In this example, COMPILER is a user-defined macro specifying the compiler being used.

#if COMPILER == "VS"


#define VARIADIC3(Name, Param, ...) Name(Param, __VA_ARGS__)
#elif COMPILER == "GCC"
#define VARIADIC3(Name, Param, ...) Name(Param, ##__VA_ARGS__)
#endif /* COMPILER */

Section 19.5: Predefined macros


Predefined macros are those that the compiler defines (in contrast to those user defines in the source file). Those
macros must not be re-defined or undefined by user.

The following macros are predefined by the C++ standard:

__LINE__ contains the line number of the line this macro is used on, and can be changed by the #line
directive.

Complete C++ Tips & Secrets for Professionals 190


__FILE__ contains the filename of the file this macro is used in, and can be changed by the #line directive.
__DATE__ contains date (in "Mmm dd yyyy" format) of the file compilation, where Mmm is formatted as if
obtained by a call to std::asctime().
__TIME__ contains time (in "hh:mm:ss" format) of the file compilation.
__cplusplus is defined by (conformant) C++ compilers while compiling C++ files. Its value is the standard
version the compiler is fully conformant with, i.e. 199711L for C++98 and C++03, 201103L for C++11 and
201402L for C++14 standard.

Version≥c++11

__STDC_HOSTED__ is defined to 1 if the implementation is hosted, or 0 if it is freestanding.

Version≥c++17

__STDCPP_DEFAULT_NEW_ALIGNMENT__ contains a size_t literal, which is the alignment used for a call to
alignment-unaware operator new.

Additionally, the following macros are allowed to be predefined by implementations, and may or may not be
present:

__STDC__ has implementation-dependent meaning, and is usually defined only when compiling a file as C, to
signify full C standard compliance. (Or never, if the compiler decides not to support this macro.)

Version≥c++11

__STDC_VERSION__ has implementation-dependent meaning, and its value is usually the C version, similarly
to how __cplusplus is the C++ version. (Or is not even defined, if the compiler decides not to support this
macro.)
__STDC_MB_MIGHT_NEQ_WC__ is defined to 1, if values of the narrow encoding of the basic character set might
not be equal to the values of their wide counterparts (e.g. if (uintmax_t)'x' != (uintmax_t)L'x')
__STDC_ISO_10646__ is defined if wchar_t is encoded as Unicode, and expands to an integer constant in the
form yyyymmL, indicating the latest Unicode revision supported.
__STDCPP_STRICT_POINTER_SAFETY__ is defined to 1, if the implementation has strict pointer safety (otherwise
it has relaxed pointer safety)
__STDCPP_THREADS__ is defined to 1, if the program can have more than one thread of execution (applicable
to freestanding implementation — hosted implementations can always have more than one thread)

It is also worth mentioning __func__, which is not an macro, but a predefined function-local variable. It contains the
name of the function it is used in, as a static character array in an implementation-defined format.

On top of those standard predefined macros, compilers can have their own set of predefined macros. One must
refer to the compiler documentation to learn those. E.g.:

gcc
Microsoft Visual C++
clang
Intel C++ Compiler

Some of the macros are just to query support of some feature:

#ifdef __cplusplus // if compiled by C++ compiler


extern "C"{ // C code has to be decorated
// C library header declarations here
}
#endif

Complete C++ Tips & Secrets for Professionals 191


Others are very useful for debugging:

Version≥c++11
bool success = doSomething( /*some arguments*/ );
if( !success ){
std::cerr << "ERROR: doSomething() failed on line " << __LINE__ - 2
<< " in function " << __func__ << "()"
<< " in file " << __FILE__
<< std::endl;
}

And others for trivial version control:

int main( int argc, char *argv[] ){


if( argc == 2 && std::string( argv[1] ) == "-v" ){
std::cout << "Hello World program\n"
<< "v 1.1\n" // I have to remember to update this manually
<< "compiled: " << __DATE__ << ' ' << __TIME__ // this updates automagically
<< std::endl;
}
else{
std::cout << "Hello World!\n";
}
}

Section 19.6: Preprocessor Operators


# operator or stringizing operator is used to convert a Macro parameter to a string literal. It can only be used with
the Macros having arguments.

// preprocessor will convert the parameter x to the string literal x


#define PRINT(x) printf(#x "\n")

PRINT(This line will be converted to string by preprocessor);


// Compiler sees
printf("This line will be converted to string by preprocessor""\n");

Compiler concatenate two strings and the final printf() argument will be a string literal with newline character at
its end.

Preprocessor will ignore the spaces before or after the macro argument. So below print statement will give us the
same result.

PRINT( This line will be converted to string by preprocessor );

If the parameter of the string literal requires an escape sequence like before a double quote() it will automatically
be inserted by the preprocessor.

PRINT(This "line" will be converted to "string" by preprocessor);


// Compiler sees
printf("This \"line\" will be converted to \"string\" by preprocessor""\n");

## operator or Token pasting operator is used to concatenate two parameters or tokens of a Macro.

// preprocessor will combine the variable and the x


#define PRINT(x) printf("variable" #x " = %d", variable##x)

int variableY = 15;


PRINT(Y);
//compiler sees
printf("variable""Y"" = %d", variableY);

Complete C++ Tips & Secrets for Professionals 192


and the final output will be

variableY = 15

Section 19.7: #pragma once


Most, but not all, C++ implementations support the #pragma once directive which ensures the file is only included
once within a single compilation. It is not part of any ISO C++ standard. For example:

// Foo.h
#pragma once

class Foo
{
};

While #pragma once avoids some problems associated with include guards, a #pragma - by definition in the
standards - is inherently a compiler-specific hook, and will be silently ignored by compilers that don't support it.
Projects which use #pragma once must be modified to be standard-compliant.

With some compilers - particularly those that employ precompiled headers - #pragma once can result in a
considerable speedup of the compilation process. Similarly, some preprocessors achieve speedup of compilation
by tracking which headers have employed include guards. The net benefit, when both #pragma once and include
guards are employed, depends on the implementation and can be either an increase or decrease of compilation
times.

#pragma once combined with include guards was the recommended layout for header files when writing MFC
based applications on windows, and was generated by Visual Studio’s add class, add dialog, add windows wizards.
Hence it is very common to find them combined in C++ Windows Applicants.

Section 19.8: Preprocessor error messages


Compile errors can be generated using the preprocessor. This is useful for a number of reasons some of which
include, notifying a user if they are on an unsupported platform or an unsupported compiler.

e.g. Return Error if gcc version is 3.0.0 or earlier.

#if __GNUC__ < 3


#error "This code requires gcc > 3.0.0"
#endif

e.g. Return Error if compiling on an Apple computer.

#ifdef __APPLE__
#error "Apple products are not supported in this release"
#endif

Chapter 20: SFINAE (Substitution Failure Is Not An


Error)
Section 20.1: What is SFINAE
SFINAE stands for Substitution Failure Is Not An Error. Ill-formed code that results from substituting types (or
values) to instantiate a function template or a class template is not a hard compile error, it is only treated as a
deduction failure.

Deduction failures on instantiating function templates or class template specializations remove that candidate from

Complete C++ Tips & Secrets for Professionals 193


the set of consideration - as if that failed candidate did not exist to begin with.

template <class T>


auto begin(T& c) -> decltype(c.begin()) { return c.begin(); }

template <class T, size_t N>


T* begin(T (&arr)[N]) { return arr; }

int vals[10];
begin(vals); // OK. The first function template substitution fails because
// vals.begin() is ill-formed. This is not an error! That function
// is just removed from consideration as a viable overload candidate,
// leaving us with the array overload.

Only substitution failures in the immediate context are considered deduction failures, all others are considered
hard errors.

template <class T>


void add_one(T& val) { val += 1; }

int i = 4;
add_one(i); // ok

std::string msg = "Hello";


add_one(msg); // error. msg += 1 is ill-formed for std::string, but this
// failure is NOT in the immediate context of substituting T

Section 20.2: void_t


Version≥C++11

void_t is a meta-function that maps any (number of) types to type void. The primary purpose of void_t is to
facilitate writing of type traits.

std::void_t will be part of C++17, but until then, it is extremely straightforward to implement:

template <class...> using void_t = void;

Some compilers require a slightly different implementation:

template <class...>
struct make_void { using type = void; };

template <typename... T>


using void_t = typename make_void<T...>::type;

The primary application of void_t is writing type traits that check validity of a statement. For example, let's check if
a type has a member function foo() that takes no arguments:

template <class T, class=void>


struct has_foo : std::false_type {};

template <class T>


struct has_foo<T, void_t<decltype(std::declval<T&>().foo())>> : std::true_type {};

How does this work? When I try to instantiate has_foo<T>::value, that will cause the compiler to try to look for the
best specialization for has_foo<T, void>. We have two options: the primary, and this secondary one which involves
having to instantiate that underlying expression:

If T does have a member function foo(), then whatever type that returns gets converted to void, and the

Complete C++ Tips & Secrets for Professionals 194


specialization is preferred to the primary based on the template partial ordering rules. So
has_foo<T>::value will be true
If T doesn't have such a member function (or it requires more than one argument), then substitution fails for
the specialization and we only have the primary template to fallback on. Hence, has_foo<T>::value is false.

A simpler case:

template<class T, class=void>
struct can_reference : std::false_type {};

template<class T>
struct can_reference<T, std::void_t<T&>> : std::true_type {};

this doesn't use std::declval or decltype.

You may notice a common pattern of a void argument. We can factor this out:

struct details {
template<template<class...>class Z, class=void, class...Ts>
struct can_apply:
std::false_type
{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:
std::true_type
{};
};

template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z, void, Ts...>;

which hides the use of std::void_t and makes can_apply act like an indicator whether the type supplied as the
first template argument is well-formed after substituting the other types into it. The previous examples may now be
rewritten using can_apply as:

template<class T>
using ref_t = T&;

template<class T>
using can_reference = can_apply<ref_t, T>; // Is T& well formed for T?

and:

template<class T>
using dot_foo_r = decltype(std::declval<T&>().foo());

template<class T>
using can_dot_foo = can_apply< dot_foo_r, T >; // Is T.foo() well formed for T?

which seems simpler than the original versions.

There are post-C++17 proposals for std traits similar to can_apply.

The utility of void_t was discovered by Walter Brown. He gave a wonderful presentation on it at CppCon 2016.

Section 20.3: enable_if


std::enable_if is a convenient utility to use boolean conditions to trigger SFINAE. It is defined as:

Complete C++ Tips & Secrets for Professionals 195


template <bool Cond, typename Result=void>
struct enable_if { };

template <typename Result>


struct enable_if<true, Result> {
using type = Result;
};

That is, enable_if<true, R>::type is an alias for R, whereas enable_if<false, T>::type is ill-formed as that
specialization of enable_if does not have a type member type.

std::enable_if can be used to constrain templates:

int negate(int i) { return -i; }

template <class F>


auto negate(F f) { return -f(); }

Here, a call to negate(1) would fail due to ambiguity. But the second overload is not intended to be used for
integral types, so we can add:

int negate(int i) { return -i; }

template <class F, class = typename std::enable_if<!std::is_arithmetic<F>::value>::type>


auto negate(F f) { return -f(); }

Now, instantiating negate<int> would result in a substitution failure since !std::is_arithmetic<int>::value is


false. Due to SFINAE, this is not a hard error, this candidate is simply removed from the overload set. As a result,
negate(1) only has one single viable candidate - which is then called.

When to use it

It's worth keeping in mind that std::enable_if is a helper on top of SFINAE, but it's not what makes SFINAE work in
the first place. Let's consider these two alternatives for implementing functionality similar to std::size, i.e. an
overload set size(arg) that produces the size of a container or array:

// for containers
template<typename Cont>
auto size1(Cont const& cont) -> decltype( cont.size() );

// for arrays
template<typename Elt, std::size_t Size>
std::size_t size1(Elt const(&arr)[Size]);

// implementation omitted
template<typename Cont>
struct is_sizeable;

// for containers
template<typename Cont, std::enable_if_t<std::is_sizeable<Cont>::value, int> = 0>
auto size2(Cont const& cont);

// for arrays
template<typename Elt, std::size_t Size>
std::size_t size2(Elt const(&arr)[Size]);

Assuming that is_sizeable is written appropriately, these two declarations should be exactly equivalent with
respect to SFINAE. Which is the easiest to write, and which is the easiest to review and understand at a glance?

Now let's consider how we might want to implement arithmetic helpers that avoid signed integer overflow in favour

Complete C++ Tips & Secrets for Professionals 196


of wrap around or modular behaviour. Which is to say that e.g. incr(i, 3) would be the same as i += 3 save for
the fact that the result would always be defined even if i is an int with value INT_MAX. These are two possible
alternatives:

// handle signed types


template<typename Int>
auto incr1(Int& target, Int amount)
-> std::void_t<int[static_cast<Int>(-1) < static_cast<Int>(0)]>;

// handle unsigned types by just doing target += amount


// since unsigned arithmetic already behaves as intended
template<typename Int>
auto incr1(Int& target, Int amount)
-> std::void_t<int[static_cast<Int>(0) < static_cast<Int>(-1)]>;

template<typename Int, std::enable_if_t<std::is_signed<Int>::value, int> = 0>


void incr2(Int& target, Int amount);

template<typename Int, std::enable_if_t<std::is_unsigned<Int>::value, int> = 0>


void incr2(Int& target, Int amount);

Once again which is the easiest to write, and which is the easiest to review and understand at a glance?

A strength of std::enable_if is how it plays with refactoring and API design. If is_sizeable<Cont>::value is
meant to reflect whether cont.size() is valid then just using the expression as it appears for size1 can be more
concise, although that could depend on whether is_sizeable would be used in several places or not. Contrast that
with std::is_signed which reflects its intention much more clearly than when its implementation leaks into the
declaration of incr1.

Section 20.4: is_detected


To generalize type_trait creation:based on SFINAE there are experimental traits detected_or, detected_t,
is_detected.

With template parameters typename Default, template <typename...> Op and typename ... Args:

is_detected: alias of std::true_type or std::false_type depending of the validity of Op<Args...>


detected_t: alias of Op<Args...> or nonesuch depending of validity of Op<Args...>.
detected_or: alias of a struct with value_t which is is_detected, and type which is Op<Args...> or Default
depending of validity of Op<Args...>

which can be implemented using std::void_t for SFINAE as following:

Version≥C++17
namespace detail {
template <class Default, class AlwaysVoid,
template<class...> class Op, class... Args>
struct detector
{
using value_t = std::false_type;
using type = Default;
};

template <class Default, template<class...> class Op, class... Args>


struct detector<Default, std::void_t<Op<Args...>>, Op, Args...>
{
using value_t = std::true_type;
using type = Op<Args...>;
};

Complete C++ Tips & Secrets for Professionals 197


} // namespace detail

// special type to indicate detection failure


struct nonesuch {
nonesuch() = delete;
~nonesuch() = delete;
n