C++Course Full
C++Course Full
C++Course Full
CERN
Foreword
What this course is not
It is not for absolute beginners
It is not for experts
It is not complete at all (would need 3 weeks...)
although it is already too long for the time we have
548 slides, 690 pages, 31 exercises...
How I see it
Adaptative pick what you want
Interactive tell me what to skip/insist on
Practical let’s spend time on real code
More courses
Outline
5 Expert C++
1 History and goals
6 Useful tools
2 Language basics
7 Concurrency
3 Object orientation (OO)
8 C++ and python
4 Core modern C++
Detailed outline
History
1978 K and R C
Classic C
Fast
compiled (unlike Java, C#, Python, ...)
allows to go close to hardware when needed
Fast
compiled (unlike Java, C#, Python, ...)
allows to go close to hardware when needed
What we get
the most powerful language
the most complicated one
the most error prone?
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 11 / 548
Intro base OO More exp Tool conc py Core Ptr NS Class/Enum Refs f() Op Control .h auto inline assert
Language basics
Inline keyword
Assertions
1 History and goals
2 Language basics
Core syntax and types
Arrays and Pointers
Scopes / namespaces
Class and enum types
References
Functions
Operators
Control structures
Headers and interfaces
Auto keyword
Inline keyword
Assertions
1 #include <iostream>
2
3 // This is a function
4 void print(int i) {
5 std::cout << "Hello, world " << i << std::endl;
6 }
7
Comments C++ 98
1 // simple comment until end of line
2 int i;
3
4 /* multiline comment
5 * in case we need to say more
6 */
7 double /* or something in between */ d;
8
9 /**
10 * Best choice : doxygen compatible comments
11 * \brief checks whether i is odd
12 * \param i input
13 * \return true if i is odd, otherwise false
14 * \see https://www.doxygen.nl/manual/docblocks.html
15 */
16 bool isOdd(int i);
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 15 / 548
Intro base OO More exp Tool conc py Core Ptr NS Class/Enum Refs f() Op Control .h auto inline assert
10 42 // int
11 42u, 42U // unsigned int
12 42l, 42L // long
13 42ul, 42UL // unsigned long
14 42ll, 42LL // long long
15 42ull, 42ULL // unsigned long long
3 std::float16_t = 3.14f16;
// 16 (1+5+10) bit float
4 std::float32_t = 3.14f32;
// like float
5 // but different type
6 std::float64_t = 3.14f64; // like double
7 // but different type
8 std::float128_t = 3.14f128; // 128 (1+15+112) bit float
9 std::bfloat_t = 3.14bf16; // 16 (1+8+7) bit float
10
2 Language basics
Core syntax and types
Arrays and Pointers
Scopes / namespaces
Class and enum types
References
Functions
Operators
Control structures
Headers and interfaces
Auto keyword
Inline keyword
Assertions
8 int i = ai[2]; // i = 3
9 char c = ac[8]; // at best garbage, may segfault
10 int i = ai[4]; // also garbage !
Pointers C++ 98
1 int i = 4;
2 int *pi = &i;
3 int j = *pi + 1;
4
10 // compile error
11 int *pak = k;
12
13 // seg fault !
14 int *pak = (int*)k;
15 int l = *pak;
Pointers C++ 98
Pointers C++ 98
Pointers C++ 98
Pointers C++ 98
Pointers C++ 98
Pointers C++ 98
Pointers C++ 98
Pointers C++ 98
nullptr C++ 11
A pointer to nothing
if a pointer doesn’t point to anything, set it to nullptr
useful to e.g. mark the end of a linked data structure
or absence of an optional function argument (pointer)
same as setting it to 0 or NULL (before C++ 11)
triggers compilation error when assigned to integer
nullptr C++ 11
A pointer to nothing
if a pointer doesn’t point to anything, set it to nullptr
useful to e.g. mark the end of a linked data structure
or absence of an optional function argument (pointer)
same as setting it to 0 or NULL (before C++ 11)
triggers compilation error when assigned to integer
Example code
1 int* ip = nullptr;
2 int i = NULL; // compiles, bug?
3 int i = nullptr; // ERROR
Scopes / namespaces
2 Language basics
Core syntax and types
Arrays and Pointers
Scopes / namespaces
Class and enum types
References
Functions
Operators
Control structures
Headers and interfaces
Auto keyword
Inline keyword
Assertions
Scope C++ 98
Definition
Portion of the source code where a given name is valid
Typically :
simple block of code, within {}
function, class, namespace
the global scope, i.e. translation unit (.cpp file + all includes)
Example
1 { int a;
2 { int b;
3 } // end of b scope
4 } // end of a scope
Namespaces C++ 98
Namespaces allow to segment your code to avoid name clashes
They can be embedded to create hierarchies (separator is ’::’)
C++ 98
1 namespace A {
2 namespace B {
3 namespace C {
4 //...
5 }
6 }
7 }
C++ 17
1 namespace A::B::C {
2 //...
3 }
Purpose
groups a number of declarations
visible only in the current translation unit
but not reusable outside
allows much better compiler optimizations and checking
e.g. unused function warning
context dependent optimizations
Supersedes static
4 static int localVar; // equivalent C code
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 34 / 548
Intro base OO More exp Tool conc py Core Ptr NS Class/Enum Refs f() Op Control .h auto inline assert
What to do instead
Qualify names: std::vector, std::cout, …
Put things that belong together in the same namespace
Use using declarations in local scopes: using std::cout;
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 35 / 548
Intro base OO More exp Tool conc py Core Ptr NS Class/Enum Refs f() Op Control .h auto inline assert
2 Language basics
Core syntax and types
Arrays and Pointers
Scopes / namespaces
Class and enum types
References
Functions
Operators
Control structures
Headers and interfaces
Auto keyword
Inline keyword
Assertions
struct C++ 98
6 Individual student;
7 student.age = 25;
8 student.weight = 78.5f;
9
10 Individual teacher = {
11 45, 67.0f
12 };
struct C++ 98
struct C++ 98
struct C++ 98
struct C++ 98
union C++ 98
“members” packed together at same memory location
1 union Duration {
2 int seconds;
3 short hours;
4 char days;
5 };
6 Duration d1, d2, d3;
7 d1.seconds = 259200;
8 d2.hours = 72;
9 d3.days = 3;
10 d1.days = 3; // d1.seconds overwritten
11 int a = d1.seconds; // d1.seconds is garbage
union C++ 98
“members” packed together at same memory location
1 union Duration {
2 int seconds;
3 short hours;
4 char days; Memory layout
5 }; 0x300C
6 Duration d1, d2, d3; 0x3008
7 d1.seconds = 259200; 0x3004
8 d2.hours = 72;
0x3000
9 d3.days = 3;
10 d1.days = 3; // d1.seconds overwritten
11 int a = d1.seconds; // d1.seconds is garbage
union C++ 98
“members” packed together at same memory location
1 union Duration {
2 int seconds;
3 short hours;
4 char days; Memory layout
5 }; 0x300C
6 Duration d1, d2, d3; 0x3008
7 d1.seconds = 259200; 0x3004
8 d2.hours = 72;
d1 259200 0x3000
9 d3.days = 3;
10 d1.days = 3; // d1.seconds overwritten
11 int a = d1.seconds; // d1.seconds is garbage
union C++ 98
“members” packed together at same memory location
1 union Duration {
2 int seconds;
3 short hours;
4 char days; Memory layout
5 }; 0x300C
6 Duration d1, d2, d3; 0x3008
7 d1.seconds = 259200; d2 72 ? ? 0x3004
8 d2.hours = 72;
d1 259200 0x3000
9 d3.days = 3;
10 d1.days = 3; // d1.seconds overwritten
11 int a = d1.seconds; // d1.seconds is garbage
union C++ 98
“members” packed together at same memory location
1 union Duration {
2 int seconds;
3 short hours;
4 char days; Memory layout
5 }; 0x300C
6 Duration d1, d2, d3; d3 3 ? ? ? 0x3008
7 d1.seconds = 259200; d2 72 ? ? 0x3004
8 d2.hours = 72;
d1 259200 0x3000
9 d3.days = 3;
10 d1.days = 3; // d1.seconds overwritten
11 int a = d1.seconds; // d1.seconds is garbage
union C++ 98
“members” packed together at same memory location
1 union Duration {
2 int seconds;
3 short hours;
4 char days; Memory layout
5 }; 0x300C
6 Duration d1, d2, d3; d3 3 ? ? ? 0x3008
7 d1.seconds = 259200; d2 72 ? ? 0x3004
8 d2.hours = 72;
d1 3 ? ? ? 0x3000
9 d3.days = 3;
10 d1.days = 3; // d1.seconds overwritten
11 int a = d1.seconds; // d1.seconds is garbage
union C++ 98
“members” packed together at same memory location
1 union Duration {
2 int seconds;
3 short hours;
4 char days; Memory layout
5 }; 0x300C
6 Duration d1, d2, d3; d3 3 ? ? ? 0x3008
7 d1.seconds = 259200; d2 72 ? ? 0x3004
8 d2.hours = 72;
d1 3 ? ? ? 0x3000
9 d3.days = 3;
10 d1.days = 3; // d1.seconds overwritten
11 int a = d1.seconds; // d1.seconds is garbage
Enums C++ 98
Only advantages
scopes enumerator names, avoids name clashes
strong typing, no automatic conversion to int
3 enum VType { Bus, Car }; enum Color { Red, Blue };
4 VType t = Bus;
5 if (t == Red) { /* We do enter */ }
6 int a = 5 * Car; // Ok, a = 5
7
8 enum class VT { Bus, Car }; enum class Col { Red, Blue };
9 VT t = VT::Bus;
10 if (t == Col::Red) { /* Compiler error */ }
11 int a = t * 5; // Compiler error
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 40 / 548
Intro base OO More exp Tool conc py Core Ptr NS Class/Enum Refs f() Op Control .h auto inline assert
6 struct Rectangle {
7 float width;
8 float height;
9 };
17 Shape s; 20 Shape t;
18 s.type = 21 t.type =
19 ShapeType::Circle; 22 Shapetype::Rectangle;
20 s.radius = 3.4; 23 t.rect.width = 3;
21 24 t.rect.height = 4;
C++ 98
1 typedef std::uint64_t myint;
2 myint count = 17;
3 typedef float position[3];
C++ 11
4 using myint = std::uint64_t;
5 myint count = 17;
6 using position = float[3];
7
References
2 Language basics
Core syntax and types
Arrays and Pointers
Scopes / namespaces
Class and enum types
References
Functions
Operators
Control structures
Headers and interfaces
Auto keyword
Inline keyword
Assertions
References C++ 98
References
References allow for direct access to another object
They can be used as shortcuts / better readability
They can be declared const to allow only read access
Example:
1 int i = 2;
2 int &iref = i; // access to i
3 iref = 3; // i is now 3
4
Specificities of reference
Natural syntax
Cannot be nullptr
Must be assigned when defined, cannot be reassigned
References to temporary objects must be const
Advantages of pointers
Can be nullptr
Can be initialized after declaration, can be reassigned
Specificities of reference
Natural syntax
Cannot be nullptr
Must be assigned when defined, cannot be reassigned
References to temporary objects must be const
Advantages of pointers
Can be nullptr
Can be initialized after declaration, can be reassigned
Functions
2 Language basics
Core syntax and types
Arrays and Pointers
Scopes / namespaces
Class and enum types
References
Functions
Operators
Control structures
Headers and interfaces
Auto keyword
Inline keyword
Assertions
Functions C++ 98
Functions C++ 98
s
11 void printRef(BigStruct &q) { s1 0x3000
12 ...
13 }
14 printRef(s); // no copy
p
7
s
11 void printRef(BigStruct &q) { s1 0x3000
12 ...
13 }
14 printRef(s); // no copy
s
11 void printRef(BigStruct &q) { s1 0x3000
12 ...
13 }
14 printRef(s); // no copy
4 void changeVal(SmallStruct p) {
5 p.a = 2; Memory layout
6 }
7 changeVal(s); 0x3008
8 // s.a == 1 0x3004
9 0x3000
10 void changeRef(SmallStruct &q) {
11 q.a = 2;
12 }
13 changeRef(s);
14 // s.a == 2
4 void changeVal(SmallStruct p) {
5 p.a = 2; Memory layout
6 }
7 changeVal(s); 0x3008
8 // s.a == 1 0x3004
9 s.a = 1 0x3000
10 void changeRef(SmallStruct &q) {
11 q.a = 2;
12 }
13 changeRef(s);
14 // s.a == 2
4 void changeVal(SmallStruct p) {
5 p.a = 2; Memory layout
6 }
7 changeVal(s); 0x3008
8 // s.a == 1 p.a = 1 0x3004
9 s.a = 1 0x3000
10 void changeRef(SmallStruct &q) {
11 q.a = 2;
12 }
13 changeRef(s);
14 // s.a == 2
4 void changeVal(SmallStruct p) {
5 p.a = 2; Memory layout
6 }
7 changeVal(s); 0x3008
8 // s.a == 1 p.a = 2 0x3004
9 s.a = 1 0x3000
10 void changeRef(SmallStruct &q) {
11 q.a = 2;
12 }
13 changeRef(s);
14 // s.a == 2
4 void changeVal(SmallStruct p) {
5 p.a = 2; Memory layout
6 }
7 changeVal(s); 0x3008
8 // s.a == 1 0x3004
9 s.a = 1 0x3000
10 void changeRef(SmallStruct &q) {
11 q.a = 2;
12 }
13 changeRef(s);
14 // s.a == 2
4 void changeVal(SmallStruct p) {
5 p.a = 2; Memory layout
6 }
7 changeVal(s); 0x3008
8 // s.a == 1 q = 0x3000 0x3004
9 s.a = 1 0x3000
10 void changeRef(SmallStruct &q) {
11 q.a = 2;
12 }
13 changeRef(s);
14 // s.a == 2
4 void changeVal(SmallStruct p) {
5 p.a = 2; Memory layout
6 }
7 changeVal(s); 0x3008
8 // s.a == 1 q = 0x3000 0x3004
9 s.a = 2 0x3000
10 void changeRef(SmallStruct &q) {
11 q.a = 2;
12 }
13 changeRef(s);
14 // s.a == 2
4 void changeVal(SmallStruct p) {
5 p.a = 2; Memory layout
6 }
7 changeVal(s); 0x3008
8 // s.a == 1 0x3004
9 s.a = 2 0x3000
10 void changeRef(SmallStruct &q) {
11 q.a = 2;
12 }
13 changeRef(s);
14 // s.a == 2
Syntax
1 struct T {...}; T a;
2 void fVal(T value); fVal(a); // by value
3 void fRef(const T &value); fRef(a); // by reference
4 void fPtr(const T *value); fPtr(&a); // by pointer
5 void fWrite(T &value); fWrite(a); // non-const ref
Overloading C++ 98
Overloading
We can have multiple functions with the same name
Must have different parameter lists
A different return type alone is not allowed
Form a so-called “overload set”
Default arguments can cause ambiguities
Functions C++ 98
Exercise: Functions
Familiarise yourself with pass by value / pass by reference.
Go to exercises/functions
Look at functions.cpp
Compile it (make) and run the program (./functions)
Work on the tasks that you find in functions.cpp
Example: Good
1 /// Count number of dilepton events in data.
2 /// \param d Dataset to search.
3 unsigned int countDileptons(Data &d) {
4 selectEventsWithMuons(d);
5 selectEventsWithElectrons(d);
6 return d.size();
7 }
6 20 // Step 4: dileptons
7 // Step 2: muons 21 int counter = 0;
8 for (....) { 22 for (....) {
9 if (...) { 23 if (...) {
10 data.erase(...); 24 counter++;
11 } 25 }
12 } 26 }
13 // Step 3: electrons 27
Operators
2 Language basics
Core syntax and types
Arrays and Pointers
Scopes / namespaces
Class and enum types
References
Functions
Operators
Control structures
Headers and interfaces
Auto keyword
Inline keyword
Assertions
Operators(1) C++ 98
Operators(1) C++ 98
Operators(1) C++ 98
Operators(2) C++ 98
Operators(2) C++ 98
Logical Operators
1 bool a = true;
2 bool b = false;
3 bool c = a && b; // false
4 bool d = a || b; // true
5 bool e = !d; // false
Operators(3) C++ 98
Comparison Operators
1 bool a = (3 == 3); // true
2 bool b = (3 != 3); // false
3 bool c = (4 < 4); // false
4 bool d = (4 <= 4); // true
5 bool e = (4 > 4); // false
6 bool f = (4 >= 4); // true
7 auto g = (5 <=> 5); // C++20 (later)
Operators(3) C++ 98
Comparison Operators
1 bool a = (3 == 3); // true
2 bool b = (3 != 3); // false
3 bool c = (4 < 4); // false
4 bool d = (4 <= 4); // true
5 bool e = (4 > 4); // false
6 bool f = (4 >= 4); // true
7 auto g = (5 <=> 5); // C++20 (later)
Precedences
c &= 1+(++b)|(a--)*4%5^7; // ???
Details can be found on cppreference
Operators(3) C++ 98
Comparison Operators
1 bool a = (3 == 3); // true
2 bool b = (3 != 3); // false
3 bool c = (4 < 4); // false
4 bool d = (4 <= 4); // true
5 bool e = (4 > 4); // false
6 bool f = (4 >= 4); // true
7 auto g = (5 <=> 5); // C++20 (later)
Precedences Avoid
c &= 1+(++b)|(a--)*4%5^7; // ???
Details can be found on cppreference
Operators(3) C++ 98
Comparison Operators
1 bool a = (3 == 3); // true
2 bool b = (3 != 3); // false
3 bool c = (4 < 4); // false
4 bool d = (4 <= 4); // true
5 bool e = (4 > 4); // false
6 bool f = (4 >= 4); // true
7 auto g = (5 <=> 5); // C++20 (later)
Control structures
2 Language basics
Core syntax and types
Arrays and Pointers
Scopes / namespaces
Class and enum types
References
Functions
Operators
Control structures
Headers and interfaces
Auto keyword
Inline keyword
Assertions
if syntax
1 if (condition1) {
2 Statement1; Statement2;
3 } else if (condition2)
4 OnlyOneStatement;
5 else {
6 Statement3;
7 Statement4;
8 }
The else and else if clauses are optional
The else if clause can be repeated
Braces are optional if there is a single statement
Practical example
1 int collatz(int a) {
2 if (a <= 0) {
3 std::cout << "not supported\n";
4 return 0;
5 } else if (a == 1) {
6 return 1;
7 } else if (a%2 == 0) {
8 return collatz(a/2);
9 } else {
10 return collatz(3*a+1);
11 }
12 }
Practical example
1 const int charge = isLepton ? -1 : 0;
Practical example
1 const int charge = isLepton ? -1 : 0;
Do not abuse it
1 int collatz(int a) {
2 return a==1 ? 1 : collatz(a%2==0 ? a/2 : 3*a+1);
3 }
Explicit ifs are generally easier to read
Use the ternary operator with short conditions and expressions
Avoid nesting
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 62 / 548
Intro base OO More exp Tool conc py Core Ptr NS Class/Enum Refs f() Op Control .h auto inline assert
Use break
Avoid switch statements with fall-through cases
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 63 / 548
Intro base OO More exp Tool conc py Core Ptr NS Class/Enum Refs f() Op Control .h auto inline assert
C++ 17
1 switch (c) {
2 case 'a':
3 f(); // Warning emitted
4 case 'b': // Warning probably suppressed
5 case 'c':
6 g();
7 [[fallthrough]]; // Warning suppressed
8 case 'd':
9 h();
10 }
Purpose
Allows to limit variable scope in if and switch statements
C++ 17
1 if (Value val = GetValue(); condition(val)) {
2 f(val); // ok
3 } else
4 g(val); // ok
5 h(val); // error, no `val` in scope here
Purpose
Allows to limit variable scope in if and switch statements
C++ 17
1 if (Value val = GetValue(); condition(val)) {
2 f(val); // ok
3 } else
4 g(val); // ok
5 h(val); // error, no `val` in scope here
C++ 98
Don’t confuse with a variable declaration as condition:
7 if (Value* val = GetValuePtr())
8 f(*val);
Practical example
4 for(int i = 0, j = 0 ; i < 10 ; i++, j = i*i) {
5 std::cout << i << "^2 is " << j << '\n';
6 }
Practical example
4 for(int i = 0, j = 0 ; i < 10 ; i++, j = i*i) {
5 std::cout << i << "^2 is " << j << '\n';
6 }
Good practice: Don’t abuse the for syntax
The for loop head should fit in 1-3 lines
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 67 / 548
Intro base OO More exp Tool conc py Core Ptr NS Class/Enum Refs f() Op Control .h auto inline assert
Reason of being
Simplifies loops over “ranges” tremendously
Especially with STL containers and ranges
Syntax
1 for ( type iteration_variable : range ) {
2 // body using iteration_variable
3 }
Example code
4 int v[4] = {1,2,3,4};
5 int sum = 0;
6 for (int a : v) { sum += a; }
Purpose
Allows to limit variable scope in range-based loops
C++ 17
1 std::array data = {"hello", ",", "world"};
2 std::size_t i = 0;
3 for (auto& d : data) {
4 std::cout << i++ << ' ' << d << '\n';
5 }
C++ 20
6 for (std::size_t i = 0; auto& d : data) {
7 std::cout << i++ << ' ' << d << '\n';
8 }
5 do {
6 statements;
7 } while(condition);
Braces are optional if the body is a single statement
5 do {
6 statements;
7 } while(condition);
Braces are optional if the body is a single statement
Bad example
1 while (n != 1)
2 if (0 == n%2) n /= 2;
3 else n = 3 * n + 1;
Bad example
1 while (1) {
2 if (n == 1) break;
3 if (0 == n%2) {
4 std::cout << n << '\n';
5 n /= 2;
6 continue;
7 }
8 n = 3 * n + 1;
9 }
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 71 / 548
Intro base OO More exp Tool conc py Core Ptr NS Class/Enum Refs f() Op Control .h auto inline assert
2 Language basics
Core syntax and types
Arrays and Pointers
Scopes / namespaces
Class and enum types
References
Functions
Operators
Control structures
Headers and interfaces
Auto keyword
Inline keyword
Assertions
Interface
Set of declarations defining some functionality
Put in a so-called “header file”
The implementation exists somewhere else
Header: hello.hpp
void printHello();
Usage: myfile.cpp
1 #include "hello.hpp"
2 int main() {
3 printHello();
4 }
Preprocessor C++ 98
1 // file inclusion
2 #include "hello.hpp"
3 // macro constants and function-style macros
4 #define MY_GOLDEN_NUMBER 1746
5 #define CHECK_GOLDEN(x) if ((x) != MY_GOLDEN_NUMBER) \
6 std::cerr << #x " was not the golden number\n";
7 // compile time or platform specific configuration
8 #if defined(USE64BITS) || defined(__GNUG__)
9 using myint = std::uint64_t;
10 #elif
11 using myint = std::uint32_t;
12 #endif
Preprocessor C++ 98
1 // file inclusion
2 #include "hello.hpp"
3 // macro constants and function-style macros
4 #define MY_GOLDEN_NUMBER 1746
5 #define CHECK_GOLDEN(x) if ((x) != MY_GOLDEN_NUMBER) \
6 std::cerr << #x " was not the golden number\n";
7 // compile time or platform specific configuration
8 #if defined(USE64BITS) || defined(__GNUG__)
9 using myint = std::uint64_t;
10 #elif
11 using myint = std::uint32_t;
12 #endif
Good practice: Use preprocessor only in very restricted cases
Conditional inclusion of headers
Customization for specific compilers/platforms
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 75 / 548
Intro base OO More exp Tool conc py Core Ptr NS Class/Enum Refs f() Op Control .h auto inline assert
Include guards
1 #ifndef MY_HEADER_INCLUDED
2 #define MY_HEADER_INCLUDED
3 ... // header file content
4 #endif
Auto keyword
2 Language basics
Core syntax and types
Arrays and Pointers
Scopes / namespaces
Class and enum types
References
Functions
Operators
Control structures
Headers and interfaces
Auto keyword
Inline keyword
Assertions
Reason of being
Many type declarations are redundant
They are often a source for compiler warnings and errors
Using auto prevents unwanted/unnecessary type conversions
1 std::vector<int> v;
2 float a = v[3]; // conversion intended?
3 int b = v.size(); // bug? unsigned to signed
Reason of being
Many type declarations are redundant
They are often a source for compiler warnings and errors
Using auto prevents unwanted/unnecessary type conversions
1 std::vector<int> v;
2 float a = v[3]; // conversion intended?
3 int b = v.size(); // bug? unsigned to signed
Practical usage
1 std::vector<int> v;
2 auto a = v[3];
3 const auto b = v.size(); // std::size_t
4 int sum{0};
5 for (auto n : v) { sum += n; }
Inline keyword
2 Language basics
Core syntax and types
Arrays and Pointers
Scopes / namespaces
Class and enum types
References
Functions
Operators
Control structures
Headers and interfaces
Auto keyword
Inline keyword
Assertions
1 // global.h
2 inline int count = 0;
3 inline const std::string filename = "output.txt";
4 // a.cpp
5 #include "global.h"
6 int f() { return count; }
7 // b.cpp
8 #include "global.h"
9 void g(int i) { count += i; }
Assertions
2 Language basics
Core syntax and types
Arrays and Pointers
Scopes / namespaces
Class and enum types
References
Functions
Operators
Control structures
Headers and interfaces
Auto keyword
Inline keyword
Assertions
Assertions C++ 98
Checking invariants in a program
An invariant is a property that is guaranteed to be true during
certain phases of a program, and the program might crash or
yield wrong results if it is violated
“Here, ‘a’ should always be positive”
This can be checked using assert
The program will be aborted if the assertion fails
Assertions C++ 98
Checking invariants in a program
An invariant is a property that is guaranteed to be true during
certain phases of a program, and the program might crash or
yield wrong results if it is violated
“Here, ‘a’ should always be positive”
This can be checked using assert
The program will be aborted if the assertion fails
Assertions C++ 98
Good practice: Assert
Assertions are mostly for developers and debugging
Use them to check important invariants of your program
Prefer handling user-facing errors with helpful error
messages/exceptions
Assertions can impact the speed of a program
Assertions are disabled when the macro NDEBUG is defined
Decide if you want to disable them when you release code
Disabling assertions
Compile a program with NDEBUG defined:
g++ -DNDEBUG -O2 -W[...] test.cpp -o test.exe
1 double f(double a) {
2 assert(a > 0.); // no effect
3 return std::sqrt(a);
4 }
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 86 / 548
Intro base OO More exp Tool conc py Core Ptr NS Class/Enum Refs f() Op Control .h auto inline assert
static_assert
1 double f(UserType a) {
2 static_assert(
3 std::is_floating_point<UserType>::value,
4 "This function expects floating-point types.");
5 return std::sqrt(a);
6 }
a.cpp: In function 'double f(UserType)':
a.cpp:3:9: error: static assertion failed: This function
expects floating-point types.
2 | static_assert(
| std::is_floating_point<UserType>::value,
| ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 87 / 548
Intro base OO More exp Tool conc py OO inherit construct static new advOO cast Op () ADL
Operator overloading
Function objects
1 History and goals
Name Lookups
2 Language basics
4 Core modern C++
3 Object orientation (OO)
5 Expert C++
Objects and Classes
Inheritance
Constructors/destructors 6 Useful tools
Static members
Allocating objects 7 Concurrency
Advanced OO
Type casting 8 C++ and python
Objects
instances of classes
1 struct MyFirstClass {
2 int a;
3 void squareA() {
4 a *= a;
5 }
6 int sum(int b) { MyFirstClass
7 return a + b; int a;
8 } void squareA();
9 }; int sum(int b);
10
11 MyFirstClass myObj;
12 myObj.a = 2;
13
14 // let's square a
15 myObj.squareA();
1 void MyFirstClass::squareA() {
2 a *= a;
3 }
4
5 int MyFirstClass::sum(int b) {
6 return a + b;
7 }
1 struct MyFirstClass {
2 int a;
3 int sum(int b);
4 int sum(int b, int c);
5 }
6
Inheritance
1 struct MyFirstClass {
2 int a;
3 void squareA() { a *= a; }
4 }; MyFirstClass
5 struct MySecondClass : int a;
6 MyFirstClass { void squareA();
7 int b;
8 int sum() { return a + b; }
9 }; MySecondClass
10
int b;
11 MySecondClass myObj2; int sum();
12 myObj2.a = 2;
13 myObj2.b = 5;
14 myObj2.squareA();
15 int i = myObj2.sum(); // i = 9
1 struct MyFirstClass {
2 int a;
3 void squareA() { a *= a; }
4 }; Memory layout
5 struct MySecondClass :
0x300C
6 MyFirstClass {
7 int b; 0x3008
b=5 0x3004
myobj2
8 int sum() { return a + b; }
9 }; a=2 0x3000
10 this pointer
11 MySecondClass myObj2;
12 myObj2.a = 2;
13 myObj2.b = 5;
14 myObj2.squareA();
15 int i = myObj2.sum(); // i = 9
Idea
make sure you cannot inherit from a given class
by declaring it final
Practically
1 struct Base final {
2 ...
3 };
4 struct Derived : Base { // compiler error
5 ...
6 };
Constructors/destructors
7 protected: 16 C::~C() {}
8 int a;
9 };
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 106 / 548
Intro base OO More exp Tool conc py OO inherit construct static new advOO cast Op () ADL
1 struct First {
2 int a;
3 First() {} // leaves a uninitialized
4 First(int a) : a(a) {}
5 };
6 struct Second : First {
7 int b;
8 Second();
9 Second(int b);
10 Second(int a, int b);
11 };
12 Second::Second() : First(), b(0) {}
13 Second::Second(int b) : b(b) {} // First() implicitly
14 Second::Second(int a, int b) : First(a), b(b) {}
Concept
special constructor called for replicating an object
takes a single parameter of type const & to class
provided by the compiler if not declared by the user
in order to forbid copy, use = delete (see next slides)
or private copy constructor with no implementation in C++ 98
Concept
special constructor called for replicating an object
takes a single parameter of type const & to class
provided by the compiler if not declared by the user
in order to forbid copy, use = delete (see next slides)
or private copy constructor with no implementation in C++ 98
1 struct C {
2 C();
3 C(const C &other);
4 };
Concept
special constructor called for replicating an object
takes a single parameter of type const & to class
provided by the compiler if not declared by the user
in order to forbid copy, use = delete (see next slides)
or private copy constructor with no implementation in C++ 98
1 struct C {
2 C();
3 C(const C &other);
4 };
Good practice: The rule of 3/5 (C++ 98/11) - cppreference
if a class needs a custom destructor, a copy/move constructor or a
copy/move assignment operator, it should have all three/five.
Concept
A constructor with a single non-default parameter can be used
by the compiler for an implicit conversion.
Example - godbolt
1 void print(const Vector & v) {
2 std::cout << "printing v elements...\n";
3 }
4
5 int main {
6 // calls Vector::Vector(int n) to construct a Vector
7 // then calls print with that Vector
8 print(3);
9 };
Concept
The keyword explicit forbids such implicit conversions.
It is recommended to use it systematically, except in special
cases.
1 class Vector {
2 public:
3 explicit Vector(int n);
4 Vector(const Vector &other);
5 ~Vector();
6 ...
7 };
Idea
avoid empty default constructors like ClassName() {}
declare them as = default
Details
without a user-defined constructor, a default one is provided
any user-defined constructor disables the default one
but the default one can be requested explicitly
rule can be more subtle depending on data members
Practically
1 Class() = default; // provide default if possible
2 Class() = delete; // disable default constructor
Idea
avoid replication of code in several constructors
by delegating to another constructor, in the initialization list
Practically
1 struct Delegate {
2 int m_i;
3 Delegate(int i) : m_i(i) {
4 ... complex initialization ...
5 }
6 Delegate() : Delegate(42) {}
7 };
Practically
1 struct Base {
2 Base(int a); // ctor 1
3 };
4 struct Derived : Base {
5 using Base::Base;
6 Derived(int a, int b); // ctor 2
7 };
8 Derived d{5}; // calls ctor 1
9 Derived d{5, 6}; // calls ctor 2
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 115 / 548
Intro base OO More exp Tool conc py OO inherit construct static new advOO cast Op () ADL
Practically
1 struct Base {
2 int a{5}; // also possible: int a = 5;
3 Base() = default;
4 Base(int _a) : a(_a) {}
5 };
6 struct Derived : Base {
7 int b{6};
8 using Base::Base;
9 };
10 Derived d1; // a = 5, b = 6
11 Derived d2{7}; // a = 7, b = 6
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 116 / 548
Intro base OO More exp Tool conc py OO inherit construct static new advOO cast Op () ADL
Static members
Static.hpp
1 class Text {
2 public:
3 static std::string upper(std::string);
4 private:
5 static int callsToUpper; // add `inline` in C++17
6 };
Static.cpp
1 #include "Static.hpp"
2 int Text::callsToUpper = 0; // required before C++17
3
Allocating objects
4 main areas
the code segment for the machine code of the executable
the data segment for global variables
the heap for dynamically allocated variables
the stack for parameters of functions and local variables
Memory layout
heap
...
...
stack
data segment
code segment
Main characteristics
allocation on the stack stays valid for the duration of the
current scope. It is destroyed when it is popped off the stack.
memory allocated on the stack is known at compile time and
can thus be accessed through a variable.
the stack is relatively small, it is not a good idea to allocate
large arrays, structures or classes
each thread in a process has its own stack
allocations on the stack are thus “thread private”
and do not introduce any thread safety issues
On the stack
objects are created on variable definition (constructor called)
objects are destructed when out of scope (destructor is called)
1 int f() {
2 MyFirstClass a{3}; // constructor called
3 ...
4 } // destructor called
5
6 int g() {
7 MyFirstClass a; // default constructor called
8 ...
9 } // destructor called
Main characteristics
Allocated memory stays allocated until it is specifically
deallocated
beware memory leaks
Dynamically allocated memory must be accessed through
pointers
large arrays, structures, or classes should be allocated here
there is a single, shared heap per process
allows to share data between threads
introduces race conditions and thread safety issues!
1 int f() {
2 // default constructor called
3 MyFirstClass *a = new MyFirstClass;
4 delete a; // destructor is called
5 }
6 int g() {
7 // constructor called
8 MyFirstClass *a = new MyFirstClass{3};
9 } // memory leak !!!
Good practice: Prefer smart pointers over new/delete
Prefer smart pointers to manage objects (discussed later)
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 127 / 548
Intro base OO More exp Tool conc py OO inherit construct static new advOO cast Op () ADL
1 int f() {
2 // default constructor called 10 times
3 MyFirstClass *a = new MyFirstClass[10];
4 ...
5 delete[] a; // destructor called 10 times
6 }
Good practice: Prefer containers over new-ed arrays
Prefer containers to manage collections of objects (discussed later)
Advanced OO
Polymorphism C++ 98
the concept
objects actually have multiple types simultaneously
and can be used as any of them
1 Polygon p; Drawable
2
6 try {
7 throw p; Polygon
8 } catch (Shape & e) {
9 // will be caught
10 }
Polymorphism C++ 98
the concept
objects actually have multiple types simultaneously
and can be used as any of them
Polymorphism C++ 98
the concept
objects actually have multiple types simultaneously
and can be used as any of them
10 } Drawable.b 0x3004
Drawable.a 0x3000
Polymorphism C++ 98
the concept
objects actually have multiple types simultaneously
and can be used as any of them
1 Polygon p; Drawable
2
private
3 int f(Drawable & d) {...}
4 f(p); // Not ok anymore Shape
5
6 try { public
7 throw p; Polygon
8 } catch (Shape & e) {
9 // ok, will be caught
10 }
the idea
a method of the parent class can be replaced in a derived class
but which one is called?
1 Polygon p; Drawable
2 p.draw(); // ? void draw();
3
4 Shape & s = p;
5 s.draw(); // ? Shape
Polygon
void draw();
the concept
methods can be declared virtual
for these, the most derived object’s implementation is used
(i.e. the dynamic type behind a pointer/reference)
for non-virtual methods, the static type of the variable decides
the concept
methods can be declared virtual
for these, the most derived object’s implementation is used
(i.e. the dynamic type behind a pointer/reference)
for non-virtual methods, the static type of the variable decides
1 Polygon p; Drawable
2 p.draw(); // Polygon.draw void draw();
3
4 Shape & s = p;
5 s.draw(); // Drawable.draw Shape
Polygon
void draw();
the concept
methods can be declared virtual
for these, the most derived object’s implementation is used
(i.e. the dynamic type behind a pointer/reference)
for non-virtual methods, the static type of the variable decides
1 Polygon p; Drawable
2 p.draw(); // Polygon.draw virtual void draw();
3
4 Shape & s = p;
5 s.draw(); // Polygon.draw Shape
Polygon
void draw();
Mechanics
virtual methods are dispatched at run time
while non-virtual methods are bound at compile time
they also imply extra storage and an extra indirection
practically, the object stores a pointer to the correct method
in a so-called “virtual table” (“vtable”)
Consequences
virtual methods are “slower” than standard ones
and they can rarely be inlined
templates are an alternative for performance-critical cases
Principle
when overriding a virtual method
the override keyword should be used
the virtual keyword is then optional
Practically
1 struct Base {
2 virtual void some_func(float);
3 };
4 struct Derived : Base {
5 void some_func(float) override;
6 };
Idea
make sure you cannot further override a given virtual method
by declaring it final
Practically
1 struct Base {
2 virtual void some_func(float);
3 };
4 struct Intermediate : Base {
5 void some_func(float) final;
6 };
7 struct Derived : Intermediate {
8 void some_func(float) override; // error
9 };
1 struct Drawable {
2 virtual void draw() = 0;
3 };
4 Drawable* getImpl();
5
6 Drawable* p = getImpl();
7 p->draw();
8 delete p;
1 struct Drawable {
2 virtual void draw() = 0;
3 };
4 std::unique_ptr<Drawable> getImpl(); // better API
5
6 auto p = getImpl();
7 p->draw();
Virtual destructors
We can mark a destructor as virtual
This selects the right destructor based on the runtime type
1 struct Drawable {
2 virtual ~Drawable() = default;
3 virtual void draw() = 0;
4 };
5 Drawable* p = getImpl(); // returns derived obj.
6 p->draw();
7 delete p; // dynamic dispatch to right destructor
Good practice: Virtual destructors
If you expect users to inherit from your class and override methods
(i.e. use your class polymorphically), declare its destructor virtual
Concept
overriding an overloaded method will hide the others
unless you inherit them using using
1 struct BaseClass {
2 virtual int foo(std::string);
3 virtual int foo(int);
4 };
5 struct DerivedClass : BaseClass {
6 using BaseClass::foo;
7 int foo(std::string) override;
8 };
9 DerivedClass dc;
10 dc.foo(4); // error if no using
Polymorphism C++ 98
Exercise: Polymorphism
go to exercises/polymorphism
look at the code
open trypoly.cpp
create a Pentagon, call its perimeter method
create a Hexagon, call its perimeter method
create a Hexagon, call its parent’s perimeter method
retry with virtual methods
Concept
one class can inherit from multiple parents
Definition
situation when one class inherits several times from a given
grand parent
Problem
are the members of the grand parent replicated?
Drawable
Rectangle Text
TextBox
TextBox TextBox
Type casting
Casts to avoid
1 void func(A const & a) {
2 A& ra = a; // Error: not const
3 A& ra = const_cast<A&>(a); // Compiles. Bad design!
4 // Evil! Don't do this:
5 B* b = reinterpret_cast<B*>(&a);
6 B* b = (B*)&a;
7 }
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 152 / 548
Intro base OO More exp Tool conc py OO inherit construct static new advOO cast Op () ADL
Operator overloading
1 struct Complex {
2 float m_real, m_imaginary;
3 Complex(float real, float imaginary);
4 Complex operator+(const Complex& other) {
5 return Complex(m_real + other.m_real,
6 m_imaginary + other.m_imaginary);
7 }
8 };
9
Symmetry
1 struct Complex {
2 float m_real, m_imaginary;
3 Complex operator+(float other) {
4 return Complex(m_real + other, m_imaginary);
5 }
6 };
7 Complex c1{2.f, 3.f};
8 Complex c2 = c1 + 4.f; // ok
9 Complex c3 = 4.f + c1; // not ok !!
Symmetry
1 struct Complex {
2 float m_real, m_imaginary;
3 Complex operator+(float other) {
4 return Complex(m_real + other, m_imaginary);
5 }
6 };
7 Complex c1{2.f, 3.f};
8 Complex c2 = c1 + 4.f; // ok
9 Complex c3 = 4.f + c1; // not ok !!
10 Complex operator+(float a, const Complex& obj) {
11 return Complex(a + obj.m_real, obj.m_imaginary);
12 }
operator+ as a friend
1 class Complex {
2 float m_r, m_i;
3 friend Complex operator+(Complex const & a, Complex const & b);
4 public:
5 Complex ( float r, float i ) : m_r(r), m_i(i) {}
6 };
7 Complex operator+(Complex const & a, Complex const & b) {
8 return Complex{ a.m_r+b.m_r, a.m_i+b.m_i };
9 }
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 158 / 548
Intro base OO More exp Tool conc py OO inherit construct static new advOO cast Op () ADL
Operators C++ 98
Exercise: Operators
Write a simple class representing a fraction and pass all tests
go to exercises/operators
look at operators.cpp
inspect main and complete the implementation of
class Fraction step by step
you can comment out parts of main to test in between
Function objects
Name Lookups
Example code
1 std::cout << std::endl;
6 namespace N {
7 class A { ... };
8 // optimized swap for A
9 void swap(A&, A&);
10 }
Lambdas
The STL
1 History and goals
More STL
Ranges
2 Language basics RAII and smart pointers
Initialization
3 Object orientation (OO)
5 Expert C++
4 Core modern C++
Constness 6 Useful tools
Constant Expressions
Exceptions
7 Concurrency
Move semantics
Copy elision
Templates 8 C++ and python
Constness
Constness C++ 98
1 int const i = 6;
2 const int i = 6; // equivalent
3
4 // error: i is constant
5 i = 5;
6
1 struct Example {
2 void foo() const {
3 // type of 'this' is 'Example const*'
4 data = 0; // Error: member function is const
5 }
6 void foo() { // ok, overload
7 data = 1; // ok, 'this' is 'Example*'
8 }
9 int data;
10 };
11 Example const e1; e1.foo(); // calls const foo
12 Example e2; e2.foo(); // calls non-const foo
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 175 / 548
Intro base OO More exp Tool conc py cst cstexpr except mv copy <T> λ STL More range RAII Init
4 int a = 0;
5 int const b = 0;
6
7 change(a); // ok
8 change(b); // error
9 read(a); // ok
10 read(b); // ok
constness C++ 98
Exercise: Constness
go to exercises/constness
open constplay.cpp
try to find out which lines won’t compile
check your guesses by compiling for real
Constant Expressions
Reason of being
use functions to compute constant expressions at compile time
Reason of being
use functions to compute constant expressions at compile time
Example
1 constexpr int f(int x) {
2 if (x > 1) return x * f(x - 1);
3 return 1;
4 }
5 constexpr int a = f(5); // computed at compile-time
6 int b = f(5); // maybe computed at compile-time
7 int n = ...; // runtime value
8 int c = f(n); // computed at runtime
Notes
A constexpr function may be executed at compile time.
Arguments must be constexpr or literals in order to allow
compile time evaluation
Function body must be visible to the compiler
A constexpr function can also be used at runtime
A constexpr variable must be initialized at compile time
Classes can have constexpr member functions
Objects used in constant expressions must be of literal type:
integral, floating-point, enum, reference, pointer type
union (of at least one) or array of literal types
class type with a constexpr constructor and the destructor is
trivial (or constexpr since C++20)
A constexpr function is implicitly inline (header files)
C++14/C++17
no try-catch, goto or asm statements
no uninitialized/static/thread_local/non-literal-type variables
C++20
no coroutines or static/thread_local/non-literal-type variables
throw and asm statements allowed, but may not be executed
transient memory allocation (memory allocated at compile-time must be
freed again at compile-time)
virtual functions and uninitialized variables allowed
C++23
no coroutines, or execution of throw and asm statements
transient memory allocation
everything else allowed
1 class DimLength {
2 float m_value;
3 public:
4 constexpr DimLength(float v, char unit):
5 m_value(toSI(v, unit)) {
6 }
7 constexpr float get(char unit) const {
8 return fromSI(m_value, unit);
9 }
10 };
11 constexpr DimLength km(1, 'k');
12 constexpr float km_y = km.get('y');
13 constexpr float km_i = km.get('i');
14 static_assert(km_y == 1093, "expected km == 1093 yards!");
Example
1 consteval int f(int x) {
2 if (x > 1) return x * f(x - 1);
3 return 1;
4 }
5 constexpr int a = f(5); // computed at compile-time
6 int b = f(5); // computed at compile-time
7 int n = ...; // runtime value
8 int c = f(n); // compilation error
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 184 / 548
Intro base OO More exp Tool conc py cst cstexpr except mv copy <T> λ STL More range RAII Init
constinit C++ 20
Motivation
Like a constexpr variable, a constinit variable guarantees
compile-time initialization, but can be modified afterwards
Only allowed for static/global and thread_local variables
Initializer must be a constant expression, but constexpr
destruction is not required
Example
1 constexpr int f(int x) {
2 if (x > 1) return x * f(x - 1);
3 return 1;
4 }
5 constexpr int a = f(5); // CT init, not modifiable
6 int b = f(5); // maybe CT init, modifiable
7 constinit int c = f(5); // CT init, modifiable
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 185 / 548
Intro base OO More exp Tool conc py cst cstexpr except mv copy <T> λ STL More range RAII Init
Exceptions
Exceptions C++ 98
Purpose
to handle exceptional events that happen rarely
and cleanly jump to a place where the error can be handled
In practice
add an exception handling block with try ... catch
when exceptions are possible and can be handled
throw an exception using throw
when a function cannot proceed or recover internally
Exceptions C++ 98
Throwing exceptions
objects of any type can be thrown (even e.g. int)
1 #include <stdexcept>
2 void process_data(file& f) {
3 if (!f.open())
4 throw std::invalid_argument{"stream is not open"};
5 auto header = read_line(f); // may throw an IO error
6 if (!header.starts_with("BEGIN"))
7 throw std::runtime_error{"invalid file content"};
8 std::string body(f.size()); // may throw std::bad_alloc
9 ...
10 }
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 188 / 548
Intro base OO More exp Tool conc py cst cstexpr except mv copy <T> λ STL More range RAII Init
Exceptions C++ 98
Standard exceptions
std::exception, defined in header <exception>
Base class of all standard exceptions
Get error message: virtual const char* what() const;
Please derive your own exception classes from this one
From <stdexcept>:
std::runtime_error, std::logic_error,
std::out_of_range, std::invalid_argument, ...
Store a string: throw std::runtime_error{"msg"}
You should use these the most
std::bad_alloc, defined in header <new>
Thrown by standard allocation functions (e.g. new)
Signals failure to allocate
Carries no message
...
Exceptions C++ 98
Catching exceptions
a catch clause catches exceptions of the same or derived type
multiple catch clauses will be matched in order
if no catch clause matches, the exception propagates
if the exception is never caught, std::terminate is called
1 try {
2 process_data(f);
3 } catch (const std::invalid_argument& e) {
4 bad_files.push_back(f);
5 } catch (const std::exception& e) {
6 std::cerr << "Failed to process file: " << e.what();
7 }
Good practice: Catching exceptions
Catch exceptions by const reference
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 190 / 548
Intro base OO More exp Tool conc py cst cstexpr except mv copy <T> λ STL More range RAII Init
Exceptions C++ 98
Rethrowing exceptions
a caught exception can be rethrown inside the catch handler
useful when we want to act on an error, but cannot handle
and want to propagate it
1 try {
2 process_data(f);
3 } catch (const std::bad_alloc& e) {
4 std::cerr << "Insufficient memory for " << f.name();
5 throw; // rethrow
6 }
Exceptions C++ 98
Catching everything
sometimes we need to catch all possible exceptions
e.g. in main, a thread, a destructor, interfacing with C, …
2 try {
3 callUnknownFramework();
4 } catch(const std::exception& e) {
5 // catches std::exception and all derived types
6 std::cerr << "Exception: " << e.what() << std::endl;
7 } catch(...) {
8 // catches everything else
9 std::cerr << "Unknown exception type" << std::endl;
10 }
Exceptions C++ 98
Stack unwinding
all objects on the stack between a throw and the matching
catch are destructed automatically
this should cleanly release intermediate resources
make sure you are using the RAII idiom for your own classes
Exceptions C++ 17
Exceptions C++ 98
A more illustrative example
exceptions are very powerful when there is much code
between the error and where the error is handled
they can also rather cleanly handle different types of errors
try/catch statements can also be nested
Exceptions C++ 98
Cost
exceptions have little cost if no exception is thrown
they are recommended to report exceptional errors
for performance, when error raising and handling are close, or
errors occur often, prefer error codes or a dedicated class
when in doubt about which error strategy is better, profile!
Avoid Prefer
for (string const &num: nums) { for (string const &num: nums) {
try { optional<int> i = convert(num);
int i = convert(num); // can if (i) {
// throw process(*i);
process(i); } else {
} catch (not_an_int const &e) { ... // log and continue
... // log and continue }
} }
}
noexcept
a function with the noexcept specifier states that it
guarantees to not throw an exception
int f() noexcept;
either no exceptions is thrown or they are handled internally
checked at compile time
allows the compiler to optimize around that knowledge
a function with noexcept(expression) is only noexcept
when expression evaluates to true at compile-time
int safe_if_8B() noexcept(sizeof(long)==8);
Exceptions C++ 98
Exception Safety Guarantees
A function can offer the following guarantees:
No guarantee
An exception will lead to undefined program state
Basic exception safety guarantee
An exception will leave the program in a defined state
At least destructors must be able to run
This is similar to the moved from state
No resources are leaked
Strong exception safety guarantee
The operation either succeeds or has no effect
When an exception occurs, the original state must be restored
This is hard to do, possibly expensive, maybe impossible
No-throw guarantee
The function never throws (e.g. is marked noexcept)
Errors are handled internally or lead to termination
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 199 / 548
Intro base OO More exp Tool conc py cst cstexpr except mv copy <T> λ STL More range RAII Init
Exceptions
Alternatives to exceptions
The global variable errno (avoid)
Return an error code (e.g. an int or an enum)
Return a std::error_code (C++ 11)
If failing to produce a value is not a hard error, consider
returning std::optional<T> (C++ 17)
Return std::expected<T, E> (C++ 23), where T is the
return type on success and E is the type on failure
Terminate the program
e.g. by calling std::terminate() or std::abort()
Contracts: Specify function pre- and postconditions. Planned
for C++ 20, but removed. Will come back in the future
Further resources
Move semantics
Inefficient code
1 void swap(std::vector<int> &a,
2 std::vector<int> &b) {
3 std::vector<int> c = a;
4 a = b;
5 b = c;
6 }
7 std::vector<int> v(10000), w(10000);
8 ...
9 swap(v, w);
Inefficient code
1 void swap(std::vector<int> &a,
2 std::vector<int> &b) {
3 std::vector<int> c = a;
4 a = b;
5 b = c;
6 }
7 std::vector<int> v(10000), w(10000);
8 ...
9 swap(v, w);
6 std::vector<int> values{...};
7 consumeVector(values); // being copied now
6 std::vector<int> values{...};
7 consumeVector(values); // being copied now
Pass by copy
One unnecessary (large?) allocation and release
Unnecessary copy of the ints
6 std::vector<int> values{...};
7 consumeVector(values); // maybe modified?
8 somethingElse(values); // safe to use values now???
So non-const references?
Non-const references could work, but does the outside know
that we’re changing the vector?
Could lead to bugs
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 205 / 548
Intro base OO More exp Tool conc py cst cstexpr except mv copy <T> λ STL More range RAII Init
6 std::vector<int> values{...};
7 consumeVector(values); // maybe modified?
8 somethingElse(values); // safe to use values now???
The idea
a new type of reference: rvalue reference
used for move semantic
denoted by &&
2 new special member functions in every class:
a move constructor similar to copy constructor
a move assignment operator similar to assignment operator
(now called copy assignment operator)
The idea
a new type of reference: rvalue reference
used for move semantic
denoted by &&
2 new special member functions in every class:
a move constructor similar to copy constructor
a move assignment operator similar to assignment operator
(now called copy assignment operator)
Practically
1 T(T const & other); // copy construction
2 T( T&& other); // move construction
3 T& operator=(T const & other); // copy assignment
4 T& operator=( T&& other); // move assignment
Practically
1 T f() { T r; return r; } // move r out of f
2 T v = f(); // move returned (temporary) T into v
3 void g(T a, T b, T c);
4 g(f(), T{}, v); // move, move, copy
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 207 / 548
Intro base OO More exp Tool conc py cst cstexpr except mv copy <T> λ STL More range RAII Init
Practically
1 class Movable {
2 Movable();
3 Movable(const Movable &other);
4 Movable(Movable &&other) noexcept :
5 Movable() { // constructor delegation
6 swap(*this, other);
7 }
8 Movable& operator=(Movable other) noexcept { // by value
9 swap(*this, other);
10 return *this;
11 }
12 friend void swap(Movable &a, Movable &b) noexcept {...}
13 };
14 Movable a, b;
15 a = b; // operator= copies b into "other"
16 a = std::move(b); // operator= moves b into "other"
Practically
1 class Movable {
2 Movable();
3 Movable(const Movable &other);
4 Movable(Movable &&other) noexcept :
5 Movable() { // constructor delegation
6 swap(*this, other);
7 }
8 Movable& operator=(const Movable& other);
9 Movable& operator=(Movable&& other) noexcept {
10 swap(*this, other);
11 return *this;
12 }
13 friend void swap(Movable &a, Movable &b) noexcept { ... }
14 };
Copy elision
Templates
Templates C++ 17
Concept
The C++ way to write reusable code
like macros, but fully integrated into the type system
Applicable to functions, classes and variables
1 template<typename T>
2 const T & max(const T &a, const T &b) {
3 return b < a ? a : b;
4 }
5 template<typename T>
6 struct Vector {
7 int m_len;
8 T* m_data;
9 };
10 template <typename T>
11 std::size_t size = sizeof(T);
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 217 / 548
Intro base OO More exp Tool conc py cst cstexpr except mv copy <T> λ STL More range RAII Init
Templates C++ 98
Warning
they are compiled for each instantiation
they need to be defined before used
so all template code must typically be in headers
or declared to be available externally (extern template)
this may lead to longer compilation times and bigger binaries
int func(int a) {
(3) return a;
1 template<typename T> func }
2 T func(T a) {
3 return a;
4 } func(5.2 double func(double a) {
) return a;
}
Templates C++ 98
Template parameters
can be types, values or other templates
you can have several
default values allowed starting at the last parameter
1 template<typename T>
2 T func(T a); // equivalent to:
3 template<class T>
4 T func(T a);
5
6 template<template<class> class C>
7 C<int> func(C<int> a); // equivalent to:
8 template<template<typename> class C>
9 C<int> func(C<int> a); // equivalent to:
10 template<template<typename> typename C> // C++17
11 C<int> func(C<int> a);
11 // out-of-line definition
12 template<typename KeyType, typename ValueType>
13 ValueType Map<KeyType, ValueType>::get
14 (const KeyType &key) {
15 ...
16 }
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 221 / 548
Intro base OO More exp Tool conc py cst cstexpr except mv copy <T> λ STL More range RAII Init
9 Polygon<19> nonadecagon{3.3f};
Specialization
Templates can be specialized for given values of their parameter
Non-deduced contexts
Deduction from certain expressions is impossible/forbidden
Non-deduced contexts
Deduction from certain expressions is impossible/forbidden
Deduction guides
Describe how constructor argument types are mapped to class
template arguments
Deduction guides
Describe how constructor argument types are mapped to class
template arguments
8 std::mutex m;
9 std::lock_guard l(m); // std::lock_guard<std::mutex>
Exercise: Templates
go to exercises/templates
look at the OrderedVector code
compile and run playwithsort.cpp. See the ordering
modify playwithsort.cpp and reuse OrderedVector with
Complex
improve OrderedVector to template the ordering
test reverse ordering of strings (from the last letter)
test order based on Manhattan distance with complex type
check the implementation of Complex
try ordering complex of complex
Lambdas
Advantages
Allows to simplify inner type definition
1 class Equation {
2 using ResultType = double;
3 ResultType evaluate();
4 }
5 Equation::ResultType Equation::evaluate() {...}
6 auto Equation::evaluate() -> ResultType {...}
Used by lambda expressions
Definition
A lambda expression is a function with no name
Definition
A lambda expression is a function with no name
Python example
1 data = [1,9,3,8,3,7,4,6,5]
2
3 # without lambdas
4 def isOdd(n):
5 return n%2 == 1
6 print(filter(isOdd, data))
7
8 # with lambdas
9 print(filter(lambda n:n%2==1, data))
Simplified syntax
1 auto f = [] (arguments) -> return_type {
2 statements;
3 };
The return type specification is optional
f is an instance of a functor type, generated by the compiler
Usage example
4 int data[]{1,2,3,4,5};
5 auto f = [](int i) {
6 std::cout << i << " squared is " << i*i << '\n';
7 };
8 for (int i : data) f(i);
Adaptable lambdas
Adapt lambda’s behaviour by accessing variables outside of it
This is called “capture”
Adaptable lambdas
Adapt lambda’s behaviour by accessing variables outside of it
This is called “capture”
Adaptable lambdas
Adapt lambda’s behaviour by accessing variables outside of it
This is called “capture”
Error
error: 'increment' is not captured
[](int x) { return x+increment; });
^
Example
1 int increment = 3;
2 int data[]{1,9,3,8,3,7,4,6,5};
3 auto f = [increment](int x) { return x+increment; };
4 for(int& i : data) i = f(i);
Code example
1 int sum = 0;
2 int data[]{1,9,3,8,3,7,4,6,5};
3 auto f = [sum](int x) { sum += x; };
4 for (int i : data) f(i);
Code example
1 int sum = 0;
2 int data[]{1,9,3,8,3,7,4,6,5};
3 auto f = [sum](int x) { sum += x; };
4 for (int i : data) f(i);
Error
error: assignment of read-only variable 'sum'
[sum](int x) { sum += x; });
Code example
1 int sum = 0;
2 int data[]{1,9,3,8,3,7,4,6,5};
3 auto f = [sum](int x) { sum += x; };
4 for (int i : data) f(i);
Error
error: assignment of read-only variable 'sum'
[sum](int x) { sum += x; });
Explanation
By default, variables are captured by value
The lambda’s operator() is const
Simple example
In order to capture by reference, add ’&’ before the variable
1 int sum = 0;
2 int data[]{1,9,3,8,3,7,4,6,5};
3 auto f = [&sum](int x) { sum += x; };
4 for (int i : data) f(i);
Simple example
In order to capture by reference, add ’&’ before the variable
1 int sum = 0;
2 int data[]{1,9,3,8,3,7,4,6,5};
3 auto f = [&sum](int x) { sum += x; };
4 for (int i : data) f(i);
Mixed case
One can of course mix values and references
5 int sum = 0, off = 1;
6 int data[]{1,9,3,8,3,7,4,6,5};
7 auto f = [&sum, off](int x) { sum += x + off; };
8 for (int i : data) f(i);
all by value
[=](...) { ... };
all by value
[=](...) { ... };
all by reference
[&](...) { ... };
all by value
[=](...) { ... };
all by reference
[&](...) { ... };
mix
[&, b](...) { ... };
[=, &b](...) { ... };
1 [ this](...) { use(*this); };
2 [ &](...) { use(*this); };
3 [&, this](...) { use(*this); };
4 [ =](...) { use(*this); }; // deprecated in C++20
5 [=, this](...) { use(*this); }; // allowed in C++20
Since the captured this is a pointer, *this refers to the object by
reference.
1 [ this](...) { use(*this); };
2 [ &](...) { use(*this); };
3 [&, this](...) { use(*this); };
4 [ =](...) { use(*this); }; // deprecated in C++20
5 [=, this](...) { use(*this); }; // allowed in C++20
Since the captured this is a pointer, *this refers to the object by
reference.
Example - godbolt
1 auto build_incrementer = [](int inc) {
2 return [inc](int value) { return value + inc; };
3 };
4 auto inc1 = build_incrementer(1);
5 auto inc10 = build_incrementer(10);
6 int i = 0;
7 i = inc1(i); // i = 1
8 i = inc10(i); // i = 11
How it works
build_incrementer returns a function object
this function’s behavior depends on a parameter
note how auto is useful here!
Examples
1 struct S {
2 decltype([](int i) { std::cout << i; }) f;
3 } s;
4 s.f(42); // prints "42"
5
6 template <typename T>
7 using CudaPtr = std::unique_ptr<T,
8 decltype([](T* p){ cudaFree(p); })>;
9
10 std::set<T, decltype([](T a, T b) { ... })> s2;
The STL
What it is
A library of standard templates
Has almost everything you need
strings, containers, iterators
algorithms, functions, sorters
functors, allocators
...
Portable
Reusable
Efficient
What it is
A library of standard templates
Has almost everything you need
strings, containers, iterators
algorithms, functions, sorters
functors, allocators
...
Portable
Reusable
Efficient
Use it
and adapt it to your needs, thanks to templates
8 std::size_t s = v.size();
9 bool empty = v.empty();
10
1 #include <unordered_map>
2 std::unordered_map<std::string, int> m;
3 m["hello"] = 1; // inserts new key, def. constr. value
4 m["hello"] = 2; // finds existing key
5 auto [it, isNewKey] = m.insert({"hello", 0}); // no effect
6 int val = m["world"]; // inserts new key (val == 0)
7 int val = m.at("monde"); // throws std::out_of_range
8
std::hash C++ 11
The standard utility to create hash codes
Used by std::unordered_map and others
Can be customized for your types via template specialization
1 #include <functional>
2 std::hash<std::string> h;
3 std::cout << h("hello"); // 2762169579135187400
4 std::cout << h("world"); // 8751027807033337960
5
Example
1 struct Incrementer {
2 int m_inc;
3 Incrementer(int inc) : m_inc(inc) {}
4
With lambdas
1 std::vector<int> v{1, 2, 3};
2 const auto inc = 42;
3 std::transform(begin(v), end(v), begin(v),
4 [inc](int value) {
5 return value + inc;
6 });
With lambdas
1 std::vector<int> v{1, 2, 3};
2 const auto inc = 42;
3 std::transform(begin(v), end(v), begin(v),
4 [inc](int value) {
5 return value + inc;
6 });
Good practice: Use STL algorithms with lambdas
Prefer lambdas over functors when using the STL
Avoid binders like std::bind2nd, std::ptr_fun, etc.
1 std::vector<int> v = ...;
2
3 // remove duplicates
4 std::sort(v.begin(), v.end());
5 auto newEndIt = std::unique(v.begin(), v.end());
6 v.erase(newEndIt, v.end());
7
8 // remove by predicate
9 auto p = [](int i) { return i > 42; };
10 auto newEndIt = std::remove_if(v.begin(), v.end(), p);
11 v.erase(newEndIt, v.end());
12
Exercise: STL
go to exercises/stl
look at the non STL code in randomize.nostl.cpp
it creates a vector of ints at regular intervals
it randomizes them
it computes differences between consecutive ints
and the mean and variance of it
open randomize.cpp and complete the “translation” to STL
see how easy it is to reuse the code with complex numbers
More STL
std::optional C++ 17
Code example
1 // address may be given for a person or not
2 void processPerson(std::string_view name,
3 std::optional<Address> address);
4 Address addr = wonderland();
5 processPerson("Alice", std::move(addr));
6
7 // Every person has a name, but not always an address
8 struct Person {
9 std::string name;
10 std::optional<Address> address;
11 };
12 std::vector<Person> ps = ...;
13 std::sort(ps.begin(), ps.end(),
14 [](const Person& a, const Person& b) {
15 return a.address < b.address;
16 }); // sorts by address, persons w/o address at front
std::optional C++ 17
Code example
1 std::variant<int, float, string> opt{100}; // holding int
2 int ival = std::get<int>(opt); // or std::get<0>(opt)
3 try {
4 float val = std::get<float>(opt) // will throw
5 } catch (std::bad_variant_access const& ex) {...}
6 if (std::holds_alternative<float>(opt))
7 std::cout << std::get<float>(opt);
8 if (const float* v = std::get_if<float>(&opt)) { ... }
9 const int current_alternative = opt.index(); // == 0
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 275 / 548
Intro base OO More exp Tool conc py cst cstexpr except mv copy <T> λ STL More range RAII Init
Practically - godbolt
1 struct Visitor {
2 auto operator()(int i) {return "i:"+std::to_string(i);}
3 auto operator()(float f) {return "f:"+std::to_string(f);}
4 auto operator()(const std::string& s) { return "s:"+s;}
5 template <typename T>
6 auto operator()(const T& t) { return std::string{"?"}; }
7 };
8 void print(std::variant<int, float, std::string, char> v) {
9 std::cout << std::visit(Visitor{}, v) << '\n';
10 }
11 print(100); print(42.0f); print("example"); print('A');
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 276 / 548
Intro base OO More exp Tool conc py cst cstexpr except mv copy <T> λ STL More range RAII Init
Practically - godbolt
1 template<typename... Ts> // covered in expert part
2 struct Overload : Ts ... { using Ts::operator()...; };
3 template<typename... Ts>
4 Overload(Ts...) -> Overload<Ts...>; // obsolete in C++20
5 int main(){
6 std::variant<int, float, std::string, char> v{ ... };
7 auto overloadSet = Overload {
8 [](int i) { std::cout << "i32:" << i;},
9 [](std::string s) { std::cout << s;},
10 [](auto a) { std::cout << "other"; },
11 };
12 std::visit(overloadSet, v);
13 }
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 277 / 548
Intro base OO More exp Tool conc py cst cstexpr except mv copy <T> λ STL More range RAII Init
Code example
1 std::any val{100}; // holding int
2 val = std::string("hello"); // holding string
3 std::string s = std::any_cast<std::string>(val);
4 try {
5 int val = std::any_cast<int>(val); // will throw
6 } catch (std::bad_any_cast const& ex) {...}
7 if (val.type() == typeid(int)) // requires RTTI
8 std::cout << std::any_cast<int>(val);
9 val.reset(); assert(!val.has_value());
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 279 / 548
Intro base OO More exp Tool conc py cst cstexpr except mv copy <T> λ STL More range RAII Init
A new syntax
5 for(auto it = begin(v); it != end(v); it++) {...}
6 for(auto i = begin(a); i != end(a); i++) {...}
A new syntax
5 for(auto it = begin(v); it != end(v); it++) {...}
6 for(auto i = begin(a); i != end(a); i++) {...}
std::tuple C++ 11
8 int& i = ...;
9 std::tuple{i}; // tuple<int>
10 std::make_tuple(i); // tuple<int>
11 std::make_tuple(std::ref(i)); // tuple<int&>
12 std::tuple{std::ref(i)};
13 // C++17 CTAD, tuple<std::reference_wrapper<int>>
Example code
1 template <typename T>
2 auto remove_ptr(T t) {
3 if constexpr (std::is_pointer_v<T>) {
4 return *t;
5 } else {
6 return t;
7 }
8 }
9 int i = ...; int *j = ...;
10 int r = remove_ptr(i); // equivalent to i
11 int q = remove_ptr(j); // equivalent to *j
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 285 / 548
Intro base OO More exp Tool conc py cst cstexpr except mv copy <T> λ STL More range RAII Init
Concept
For generating pseudo-random numbers the STL offers:
engines and engine adaptors to generate pseudo random bits
distributions to shape these into numbers
access to (potential) hardware entropy
All found in header <random>
Example
1 #include <random>
2 std::random_device rd;
3 std::default_random_engine engine{rd()};
4 std::normal_distribution dist(5.0, 1.5); // µ, σ
5 double r = dist(engine);
Engines C++ 11
Engines
Generate random bits (as integers), depending on algorithm
Can be seeded via constructor or seed()
Algorithms: linear_congruential_engine,
mersenne_twister_engine, subtract_with_carry_engine
Adaptors: discard_block_engine, independent_bits_engine,
shuffle_order_engine
Aliases: minstd_rand0, minstd_rand, mt19937, mt19937_64,
ranlux24, ranlux48, knuth_b (use these)
std::default_random_engine aliases one of the above
std::random_device
Provides non-deterministic random numbers
Uses hardware provided entropy, if available
May become slow when called too often,
e.g. when hardware entropy pool is exhausted
Use only to seed engine, if non-deterministic numbers needed
Distributions C++ 11
Distributions
Take random bits from engines and shape them into numbers
Standard library provides a lot of them
Uniform, bernoulli, poisson, normal and sampling distributions
See cppreference for the full list
Disadvantages
Seeding is not thread-safe; rand() may be.
Returned numbers have small, implementation-defined range
No guarantee on quality
Mapping a random number to a custom range is hard
E.g.: rand() % 10 yields biased numbers and is wrong!
Ranges
Ranges C++ 20
Variations on ranges
There are actually different types of ranges
input_range, output_range, forward_range,
bidirectional_range, random_access_range,
contiguous_range
corresponding to the different iterator categories
Views C++ 20
The view concept
A view is a non-owning object built from a range or another
view after applying some range adaptor(s)
The time complexity to copy, move, assign is constant
Range adaptors can be applied/chained with | and are lazy
They live in the std::views namespace
They introduce easy functional programming to the STL
Example - godbolt
1 auto const numbers = std::views::iota(0, 6);
2 auto even = [](int i) { return 0 == i % 2; };
3 auto square = [](int i) { return i * i; };
4 auto results = numbers | std::views::filter(even)
5 | std::views::transform(square);
6 for (auto a : results) { ... } // 0, 4, 16
Useful subset
all a view on all elements
filter applies a filter
transform applies a transformation
take takes only first n elements
drop skips first n elements
join/split joins ranges or splits into subranges
reverse reverses view order
elements for tuple views, extract nth elements of each tuple
keys/values for pair like views, takes 1st/2nd element of each pair
Example - godbolt
1 // print first 20 prime numbers above 1000000
2 for (int i: std::views::iota(1000000)
3 | std::views::filter(odd)
4 | std::views::filter(isPrime)
5 | std::views::take(20)) {
6 std::cout << i << " ";
7 }
Practically
Use variable construction/destruction and scope semantics:
wrap the resource inside a class
acquire resource in constructor
release resource in destructor
create an instance on the stack
automatically destructed when leaving the scope
including in case of exception
use move semantics to pass the resource around
5 // file usage
6 logfile.write("hello logfile!"); // may throw
7
std::unique_ptr C++ 11
A RAII pointer
wraps and behaves like a regular pointer
get underlying pointer using get()
when destroyed, deletes the object pointed to
has move-only semantic
the pointer has unique ownership
copying will result in a compile error
std::unique_ptr C++ 11
A RAII pointer
wraps and behaves like a regular pointer
get underlying pointer using get()
when destroyed, deletes the object pointed to
has move-only semantic
the pointer has unique ownership
copying will result in a compile error
1 #include <memory>
2 void f(std::unique_ptr<Foo> ptr) {
3 ptr->bar();
4 } // deallocation when f exits
5
Quiz C++ 11
Quiz C++ 11
std::make_unique C++ 14
std::make_unique
allocates and constructs an object with arguments
and wraps it with std::unique_ptr in one step
no new or delete calls anymore!
no memory leaks if used consistently
std::make_unique C++ 14
std::make_unique
allocates and constructs an object with arguments
and wraps it with std::unique_ptr in one step
no new or delete calls anymore!
no memory leaks if used consistently
std::make_unique usage
1 {
2 // calls new File("logfile.txt") internally
3 auto f = std::make_unique<File>("logfile.txt");
4 f->write("hello logfile!");
5 } // deallocation at end of scope
Dynamic arrays
unique_ptr can wrap arrays
Use T[] as template parameter
This will enable the subscript operator
The default constructor is called for each element
If size known at compile time, prefer std::array
If size might change, prefer std::vector
1 auto b = std::make_unique<Foo[]>(10);
2 b[3] = ...;
3 b[4].someFunction();
4
A question of ownership
1 std::unique_ptr<T> produce();
2 void observe(const T&);
3 void modifyRef(T&);
4 void modifyPtr(T*);
5 void consume(std::unique_ptr<T>);
6 std::unique_ptr<T> pt{produce()}; // Receive ownership
7 observe(*pt); // Keep ownership
8 modifyRef(*pt); // Keep ownership
9 modifyPtr(pt.get()); // Keep ownership
10 consume(std::move(pt)); // Transfer ownership
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 306 / 548
Intro base OO More exp Tool conc py cst cstexpr except mv copy <T> λ STL More range RAII Init
std::shared_ptr C++ 11
weak_ptr C++ 11
weak_ptr: a non-owning observer
Sometimes want to observe resources without keeping them alive
shared_ptr? Resource stays alive
Raw pointer? Risk of dangling pointer
The solution is to construct a weak_ptr from a shared pointer
To access the resource, convert the weak into a shared_ptr
1 std::shared_ptr<Cache> getSharedCache();
2 std::weak_ptr<Cache> weakPtr{ getSharedCache() };
3 // ... shared cache may be invalidated here
4 if (std::shared_ptr<Cache> cache = weakPtr.lock()) {
5 // Cache is alive, we actively extend its lifetime
6 return cache->findItem(...);
7 } else {
8 // Cache is nullptr, we need to do something
9 weakPtr = recomputeCache(...);
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 309 / 548
Intro base OO More exp Tool conc py cst cstexpr except mv copy <T> λ STL More range RAII Init
Initialization
Initialization
Initializing scalars
1 int i(42); // direct initialization
2 int i{42}; // direct list initialization (C++11)
3 int i = 42; // copy initialization
4 int i = {42}; // copy list initialization (C++11)
All of the above have the same effect: i == 42
Narrowing conversions
1 int i(42.3); // i == 42
2 int i{42.3}; // compilation error
3 int i = 42.3; // i == 42
4 int i = {42.3}; // compilation error
Braced initialization prevents narrowing conversions
Initialization
Initializing class types with constructors
1 struct C {
2 C(int i);
3 C(int i, int j);
4 };
5
Initialization
std::initializer_list
1 struct C {
2 C(int i, int j);
3 C(std::initializer_list<int> l);
4 };
5
Beware
1 std::vector<int> v1(4, 5); // {5, 5, 5, 5}
2 std::vector<int> v2{4, 5}; // {4, 5}
Initialization
Type deduction
1 auto i(42); // int
2 auto i{42}; // C++11: std::initializer_list<int>
3 // since C++17: int
4 auto i = 42; // int
5 auto i = {42}; // std::initializer_list<int>
Initialization C++ 11
Default initialization
T t;
If T has a default constructor (including compiler generated),
calls it
If T is a fundamental type, t is left uninitialized (undefined
value)
If T is an array, the same rules are applied to each item
Initialization C++ 11
Value initialization
1 T t{}; // value initialization
2 T t = {}; // same
3 f(T()); // passes value-initialized temporary
4 f(T{}); // same
5 T t(); // function declaration
Basically zero-initializes t (or the temporary), unless T has a
user defined constructor, which is run in this case
Details on cppreference
Initialization C++ 11
Aggregate initialization
1 struct S { int i; double d; };
2
Initialization C++ 11
Initialization
Further resources
The Nightmare of Initialization in C++ - Nicolai Josuttis -
CppCon 2018
Initialization in modern C++ - Timur Doumler - Meeting
C++ 2018
Variadic templates
Perfect forwarding
1 History and goals
SFINAE
Concepts
2 Language basics Modules
Coroutines
3 Object orientation (OO)
6 Useful tools
4 Core modern C++
7 Concurrency
5 Expert C++
The <=> operator 8 C++ and python
5 Expert C++
The <=> operator
Variadic templates
Perfect forwarding
SFINAE
Concepts
Modules
Coroutines
Motivation
One often needs operator< for a user-defined class
e.g. when sorting a std::vector
e.g. when using it as a key for std::set or std::map
Many other operators are also desirable for completeness
operator>, operator>=, operator<=, ...
often implemented reusing e.g. operator< and operator==
Should be defined as hidden friend functions
Much boilerplate code to write. Too much...
Idea
C++20 introduces operator<=>
named the three-way comparison operator.
unofficially called spaceship operator
allowing to implement all comparisons in one go
How it works
it returns something which can be compared to 0
similar to std::strcmp
lower, greater, or equal to 0 means respectively lower than,
greater than and equivalent to
It is provided by default for all built-in types and many types
in the standard library
3 types of ordering
strong exactly one test among <0, ==0, and >0 will return true
weak like strong but two equivalent values may differ
they are however equivalent for ranking
e.g. rational numbers 2/3 and 4/6
partial like weak but some values are incomparable
for some values all tests may return false
e.g. compare a floating point to NaN
Summary
Defining operator<=> allows you to use operator<,
operator>, operator<=, and operator>= for free
The standard library defines a few kinds of orderings
strong, weak and partial
If operator<=> does not define a strong ordering, avoid
defining operator==
Variadic templates
5 Expert C++
The <=> operator
Variadic templates
Perfect forwarding
SFINAE
Concepts
Modules
Coroutines
4 template<typename T,
5 typename... Args> // temp. param. pack
6 T sum(T first, Args... args) { // func. param. pack
7 return first + sum(args...); // pack expansion
8 }
9 int s = sum(1, 2, 3, 8, 7);
About performance
do not be afraid of recursion
everything is at compile time!
unlike C-style variadic functions
e.g. printf(const char* fmt, ...);
1 template<typename... Args>
2 void f(Args... args) {
3 constexpr std::size_t N = sizeof...(Args);
4 std::tuple<Args...> t{args...};
5 }
6 f(0, 1, 2); // Args = [int, int, int]
7 f(); // Args = empty
8 f(0, true, 3.14); // Args = [int, bool, double]
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 338 / 548
Intro base OO More exp Tool conc py spaceship tmpl forward sfinae concepts modules Coroutines
Example
1 template<typename... Args>
2 auto sum1(Args... args) {
3 return (args + ...); // unary fold over +
4 } // parentheses mandatory
5 template<typename... Args>
6 auto sum2(Args... args) {
7 return (args + ... + 0); // binary fold over +
8 } // parentheses mandatory
9 int sum = sum1(); // error
10 int sum = sum2(); // ok
std::integer_sequence C++ 14
Packs of values
We can also have non-type template parameter packs
Useful to pass lists of compile-time constants around
The standard library includes a few helpers
4 template<size_t... Is>
5 using index_sequence =
6 integer_sequence<std::size_t, Is...>;
7
8 template<size_t N>
9 using make_index_sequence =
10 index_sequence</*0, 1, ..., N-1*/ >;
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 342 / 548
Intro base OO More exp Tool conc py spaceship tmpl forward sfinae concepts modules Coroutines
std::integer_sequence C++ 14
Example - make_from_tuple with helper
1 template<typename T,
2 typename... Args, std::size_t... Is>
3 T helper(std::tuple<Args...> args,
4 std::index_sequence<Is...>) {
5 return T(std::get<Is>(args)...);
6 }
7 template<typename T, typename... Args>
8 T make_from_tuple(std::tuple<Args...> args) {
9 return helper<T>(args,
10 std::make_index_sequence<sizeof...(Args)>{});
11 }
12
std::integer_sequence C++ 20
Perfect forwarding
5 Expert C++
The <=> operator
Variadic templates
Perfect forwarding
SFINAE
Concepts
Modules
Coroutines
Example usage :
emplace_back
make_unique
and const T& won’t work when passing something non const
Exploding complexity
for n arguments, 3n overloads
you do not want to try n = 5...
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 351 / 548
Intro base OO More exp Tool conc py spaceship tmpl forward sfinae concepts modules Coroutines
Reference collapsing
Rule: When multiple references are involved, & always wins
T&& &, T& &&, T& & → T&
T&& && → T&&
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 352 / 548
Intro base OO More exp Tool conc py spaceship tmpl forward sfinae concepts modules Coroutines
Forwarding reference
Next to a template parameter, that can be deduced, && is not
an rvalue reference, but a “forwarding reference”
aka. “universal reference”
So, this applies only to functions
The template parameter is additionally allowed to be deduced
as an lvalue reference, and reference collapsing proceeds:
if an lvalue of type U is given, T is deduced as U& and the
parameter type U& && collapses to U&
otherwise (rvalue), T is deduced as U
and forms the parameter type U&&
Examples
1 template <typename T>
2 void f(T&& t) { ... }
3
Careful!
1 template <typename T> struct S {
2 S(T&& t) { ... } // rvalue references
3 void f(T&& t) { ... } // NOT forwarding references
4 };
Careful!
1 template <typename T> struct S {
2 S(T&& t) { ... } // rvalue references
3 void f(T&& t) { ... } // NOT forwarding references
4 };
Correct version
1 template <typename T> struct S {
2 template <typename U, std::enable_if_t<
3 std::is_same_v<std::decay_t<U>, T>, int> = 0>
4 S(U&& t) { ... } // deducing context -> fwd. ref.
5 template <typename U>
6 void f(U&& t) // deducing context -> fwd. ref.
7 requires std::same_as<std::decay_t<U>, T> { ... }
8 };
1 template<typename T>
2 T&& forward(remove_reference_t<T>& t) noexcept { // 1.
3 return static_cast<T&&>(t);
4 }
5 template<typename T>
6 T&& forward(remove_reference_t<T>&& t) noexcept { // 2.
7 return static_cast<T&&>(t);
8 }
SFINAE
5 Expert C++
The <=> operator
Variadic templates
Perfect forwarding
SFINAE
Concepts
Modules
Coroutines
Example
1 template <typename T>
2 void f(typename T::type arg) { ... }
3 void f(int a) { ... }
4
decltype C++ 11
The main idea
gives the type of the result of an expression
the expression is not evaluated (i.e. unevaluated context)
at compile time
Example
1 struct A { double x; };
2 A a;
3 decltype(a.x) y; // double
4 decltype((a.x)) z = y; // double& (lvalue)
5 decltype(1 + 2u) i = 4; // unsigned int
6
declval C++ 11
The main idea
gives you a reference to a “fake” object at compile time
useful for types that cannot easily be constructed
use only in unevaluated contexts, e.g. inside decltype
1 struct Default {
2 int foo() const;
3 };
4 class NonDefault {
5 private: NonDefault();
6 public: int foo() const;
7 };
8 decltype(Default().foo()) n1 = 1; // int
9 decltype(NonDefault().foo()) n2 = 2; // error
10 decltype(std::declval<NonDefault>().foo()) n3 = 3;
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 363 / 548
Intro base OO More exp Tool conc py spaceship tmpl forward sfinae concepts modules Coroutines
Example
1 struct truth : std::true_type { };
2
Possible implementation
1 using true_type = integral_constant<bool, true >;
2 using false_type = integral_constant<bool, false>;
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 364 / 548
Intro base OO More exp Tool conc py spaceship tmpl forward sfinae concepts modules Coroutines
Example
1 template <typename T, typename = void>
2 struct hasFoo : std::false_type {}; // primary template
3 template <typename T>
4 struct hasFoo<T, decltype(std::declval<T>().foo())>
5 : std::true_type {}; // template special.
6 struct A{}; struct B{ void foo(); };
7 static_assert(!hasFoo<A>::value, "A has no foo()");
8 static_assert(hasFoo<B>::value, "B has foo()");
Example - godbolt
1 template <typename T, typename = void>
2 struct hasFoo : std::false_type {};
3 template <typename T>
4 struct hasFoo<T, decltype(std::declval<T>().foo())>
5 : std::true_type {};
6
7 struct A{};
8 struct B{ void foo(); };
9 struct C{ int foo(); };
10
11 static_assert(!hasFoo<A>::value, "A has no foo()");
12 static_assert(hasFoo<B>::value, "B has foo()");
13 static_assert(!hasFoo<C>::value, "C has no foo()");
14 static_assert(hasFoo<C,int>::value, "C has foo()");
Concept
Maps a sequence of given types to void
Introduced in C++ 17, though trivial to implement in C++ 11
Can be used in specializations to check the validity of an
expression
12 static_assert(!hasFoo<A>::value,"unexpected foo()");
13 static_assert(hasFoo<B>::value, "expected foo()");
14 static_assert(hasFoo<C>::value, "expected foo()");
enable_if cppreference
template<bool B, typename T=void>
struct enable_if;
If B is true, has a nested alias type to type T
otherwise, has no such alias
Example
1 template<bool B, typename T=void>
2 struct enable_if {};
3 template<typename T>
4 struct enable_if<true, T> { using type = T; };
5
Concepts
5 Expert C++
The <=> operator
Variadic templates
Perfect forwarding
SFINAE
Concepts
Modules
Coroutines
Motivation
Generic programming is made of variable, function and class
templates which can be instantiated with different types.
It is frequent to instantiate them with unsuited types, and
the resulting compilation errors are cryptic.
As a last resort, authors provide documentation, and
practice tricky template meta-programming.
C++20 brings simpler ways to define constraints on
template parameters.
A new keyword
The keyword requires lets us define various constraints.
A new keyword
The keyword requires lets us define various constraints.
Concepts C++ 20
Definition
a concept gives a name to a given set of requirements
useful when the same requirements are reused frequently
1 template<MyFloatingPoint T>
2 bool equal( T e1, T e2 ) {
3 return std::abs(e1-e2)<std::numeric_limits<T>::epsilon();
4 }
Constrained variables
a concept can be used together with auto to impose
requirements on the type of a variable
Example code
4 MyFloatingPoint auto f = 3.14f;
5 MyFloatingPoint auto d = 3.14;
6 MyFloatingPoint auto i = 3; // compile error
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 381 / 548
Intro base OO More exp Tool conc py spaceship tmpl forward sfinae concepts modules Coroutines
Practically
1 template<typename T>
2 concept StreamableAndComparableNumber =
3 requires( T v1, T v2, std::ostream os ) {
4 requires std::integral<T> || std::floating_point<T>;
5 os<<v1<<v2;
6 { equal(v1,v2) } -> std::convertible_to<bool>;
7 };
Summary
A template can now require properties of its parameters
Compiler error messages clearly state which argument does
not fulfill which requirement
A set of requirements can be gathered in a concept
Overload resolution takes requirements into account
The standard library provides many ready-to-use concepts
Writing a new good concept is an expert task
Exercise: Concepts
go to exercises/concepts
you will use concepts to optimize some loop on iterators
follow the instructions in the source code
Modules
5 Expert C++
The <=> operator
Variadic templates
Perfect forwarding
SFINAE
Concepts
Modules
Coroutines
Modules C++ 20
Motivation
Modules allow for sharing declarations and definitions across
translation units
Header files do the same, but modules have benefits:
Better isolation. No cross-talk between multiple pieces of
included code, and also not between included code and your
code.
Better control over public interface of your library. Avoid
leaking library dependencies into user code.
Faster compilation (import fmt; >1500x faster)
Textual includes
1 #include "math.h"
Module importation
1 import math;
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 389 / 548
Intro base OO More exp Tool conc py spaceship tmpl forward sfinae concepts modules Coroutines
main.cpp
1 import <iostream>;
2 import math;
3 int main() {
4 auto r = utils::Ratio{1, 3};
5 std::cout << utils::toString(r);
6 }
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 390 / 548
Intro base OO More exp Tool conc py spaceship tmpl forward sfinae concepts modules Coroutines
Exporting declarations
By default, everything inside a module is internal
Declarations become visible when declared with export in the
primary module interface
You can only export entities at namespace scope (including
the global scope)
You cannot export entities with internal linkage (declared
static or inside anonymous namespace)
You cannot export aliases to non-exported types
3 namespace {
4 export void f(); // error
5 export float pi = 3.14f; // error
6 export struct Ratio { ... }; // error
7 }
8
12 class C { ... };
13 export using D = C; // error
main.cpp
1 import math;
2 import <iostream>;
3 int main() {
4 Ratio r = golden(); // error: Ratio not visible
5 auto r = golden(); // OK: Ratio is reachable
6 std::cout << r.num; // OK: members of reachable
7 // types are visible
8 using R = decltype(r);
9 R r2{1.f, 3.f}; // OK
10 }
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 394 / 548
Intro base OO More exp Tool conc py spaceship tmpl forward sfinae concepts modules Coroutines
Interface
1 export module math;
2
Implementation
1 module;
2 #include <cassert>
3 module math;
4 import <string>;
5
6 S f() { ... }
7 std::string b() { ... }
Implementation
1 module math;
2 import <string>;
3
4 S f() { ... }
5 std::string b() { ... }
Interface
1 export module math;
2 import :foo; // no export
3 import :bar; // no export
4 import <string>;
5
Purpose
For easier transition from header-based projects to modules
Allows to import any header file as if it was a module
Can be mixed with regular header inclusion
1 #include <set>
2 import <vector>;
3
4 #include "header.hpp"
5 import "otherheader.hpp";
Effect
All declarations in the imported header will be exported
An imported header is not affected by the preprocessor state.
Thus, you cannot “configure” the header by defining a macro
before importing. This allows the header to be precompiled.
An imported header may still provide preprocessor macros
1 #define MY_MACRO 1
2 #include "header.hpp" // may be affected by MY_MACRO
3 import "otherheader.hpp"; // ignores MY_MACRO
Importable headers
A header file which can successfully be either included or
imported is called “importable”
I.e. the header does not require setup of preprocessor state
before inclusion
All C++ standard library headers are importable
C wrapper headers (<c*>) are not
Include translation
Allows the compiler to automatically turn includes into
imports
Controlled via compiler flags and the build system
Basically a standard replacement for precompiled headers
Build system
h.hpp Traditional workflow
#include <vector> <vector>
...
#
a.cpp
h.hpp # #
#include "h.hpp"
... # #
#include "h.hpp"
a.o b.o c.o
...
c.cpp Linker
#include <vector>
... binary
a.cpp vector.bmi #
... binary
Build system
New challenges
Resolving a module name to the file name building it
Translation units no longer independent
Extra tool needs to infer dependency graph before building
Called a “dependency scanner”
Needs to be communicated to the build system
Using a new standard dependency file format
Parallel and distributed builds need synchronization
Compilation of module translation units produces a binary
module interface (BMI) and an object file
Need to manage BMIs between multiple compiler invocations
Tools beside the compiler need to build/read BMIs
E.g. static analysis, language servers for auto completion, etc.
The C++ standard specifies very little on how this works
We may experience large implementation divergence
Status
Dependency scanner since clang 16: clang-scan-deps
Experimental module support in cmake since 3.26, uses
clang-scan-deps, many fixes in later versions
MSBuild (Windows) fully handles dependency scanning
g++ a.cpp b.cpp ... “just works”, must be one line though
Experimental extensions to GNU make exist to grow
dependency graph on the fly while modules are discovered
Header units unsupported in all build systems (May 2023)
C++23: import std; supported in MSVC, partially in clang
GCC/clang/MSVC also plan support in C++20
Modules C++ 20
Modules C++ 20
Resources
A (Short) Tour of C++ Modules - Daniela Engert - CppCon 2021
(very technical)
Understanding C++ Modules: Part1, Part2 and Part3.
So, You Want to Use C++ Modules … Cross-Platform? - Daniela
Engert - C++ on Sea 2023
import CMake: 2023 State of C++20 modules in CMake - Bill
Hoffman - CppNow 2023
Modules C++ 20
Exercise: Modules
go to exercises/modules
convert the Complex.hpp header into a module named math
Coroutines
5 Expert C++
The <=> operator
Variadic templates
Perfect forwarding
SFINAE
Concepts
Modules
Coroutines
Requirements
someType needs to be some iterable type
range should execute lazily, only creating values on demand
meaning range should be suspended and resumed
That’s what coroutines are for!
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 414 / 548
Intro base OO More exp Tool conc py spaceship tmpl forward sfinae concepts modules Coroutines
++
What C 20 allows C++ 20
Definition of a coroutine
a function which can be suspended and resumed
potentially by another caller (even on a different thread)
practically a function using one of:
co_await suspends execution
co_yield suspends execution and returns a value
co_return completes execution
Implications
on suspension, the state of the function needs to be preserved
the suspended function becomes pure data on the heap
access to this data is given via the returned value
so the returned type must follow some convention
it needs to contain a struct promise_type
promise_type interface
get_return_object builds a Task from the given promise
initial/final_suspend called before/after coroutine starts/ends
unhandled_exception called in case of exception in coroutine
return_void/value returns final value of the coroutine
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 417 / 548
Intro base OO More exp Tool conc py spaceship tmpl forward sfinae concepts modules Coroutines
allocate coroutine state on the heap construct promise inside coroutine state
promise.return_void() promise.final_suspend()
Credits : original idea for this graph comes from this web site
Code
1 Task myCoroutine() {
2 std::cout << "Step 1 of coroutine\n";
3 co_await std::suspend_always{};
4 std::cout << "Step 2 of coroutine\n";
5 co_await std::suspend_always{};
6 std::cout << "final step\n";
7 }
Code
1 struct Task {
2 struct promise_type {
3 Task get_return_object() {
4 // build Task with the coroutine handle
5 return {std::coroutine_handle<promise_type>::from_promise(*this)};
6 }
7 ... // rest is unchanged
8 };
9 std::coroutine_handle<promise_type> h_;
10 void resume() { h_.resume(); }
11 };
Step 1 of coroutine
In main, between Step 1 and 2
Step 2 of coroutine
In main, between Step 2 and final step
final step
Typical code
1 template<typename T> struct Task {
2 struct promise_type {
3 T value_;
4 std::suspend_always yield_value(T&& from) {
5 value_ = std::forward<T>(from); // caching the result
6 return {};
7 }
8 ... // rest is unchanged
9 };
10 T getValue() { return h_.promise().value_; } // accessor for user code
11 };
awaitables vs awaiters
awaitable is the type required by co_await/co_yield
or the expression given has to be convertible to awaitable
alternatively the promise_type can have an
await_transform method returning an awaitable
an awaiter is retrieved from the awaitable by the compiler
either by calling operator co_wait on it
or via direct conversion if no such operator exists
an awaiter has 3 main methods
await_ready, await_suspend and await_resume
true false
awaiter.await_ready() coroutine suspended
false
awaiter.await_suspend(handle)
void
coroutine
handle true
Useful tools
Version control
Code formatting
1 History and goals
The Compiling Chain
Web tools
2 Language basics Debugging
Sanitizers
3 Object orientation (OO) The Valgrind family
Static code analysis
4 Core modern C++ Profiling
Doxygen
5 Expert C++
7 Concurrency
6 Useful tools
C++ editor 8 C++ and python
6 Useful tools
C++ editor
Version control
Code formatting
The Compiling Chain
Web tools
Debugging
Sanitizers
The Valgrind family
Static code analysis
Profiling
Doxygen
Version control
6 Useful tools
C++ editor
Version control
Code formatting
The Compiling Chain
Web tools
Debugging
Sanitizers
The Valgrind family
Static code analysis
Profiling
Doxygen
Version control
A few tools
git THE mainstream choice. Fast, light, easy to use
mercurial The alternative to git
Bazaar Another alternative
Subversion Historical, not distributed - don’t use
CVS Archeological, not distributed - don’t use
Code formatting
6 Useful tools
C++ editor
Version control
Code formatting
The Compiling Chain
Web tools
Debugging
Sanitizers
The Valgrind family
Static code analysis
Profiling
Doxygen
clang-format
.clang-format
File describing your formatting preferences
Should be checked-in at the repository root (project wide)
clang-format -style=LLVM -dump-config >
.clang-format
Adapt style options with help from: https://clang.llvm.
org/docs/ClangFormatStyleOptions.html
Run clang-format
clang-format --style=LLVM -i <file.cpp>
clang-format -i <file.cpp> (looks for .clang-format file)
git clang-format (formats local changes)
git clang-format <ref> (formats changes since git <ref>)
Some editors/IDEs find a .clang-format file and adapt
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 439 / 548
Intro base OO More exp Tool conc py edit VCS format gcc web gdb sani valgrind static prof doxygen
clang-format
Exercise: clang-format
Go to any example
Format code with:
clang-format --style=GNU -i <file.cpp>
Inspect changes, try git diff .
Revert changes using git checkout -- <file.cpp> or
git checkout .
Go to exercises directory and create a .clang-format file
clang-format -style=LLVM -dump-config >
.clang-format
Run clang-format -i <any_exercise>/*.cpp
Revert changes using git checkout <any_exercise>
6 Useful tools
C++ editor
Version control
Code formatting
The Compiling Chain
Web tools
Debugging
Sanitizers
The Valgrind family
Static code analysis
Profiling
Doxygen
The steps
cpp the preprocessor
handles the # directives (macros, includes)
creates “complete” source code (ie. translation unit)
g++ the compiler
creates machine code from C++ code
ld the linker
links several binary files into libraries and executables
Compilers
Available tools
gcc the most common and most used
free and open source
clang drop-in replacement of gcc
slightly better error reporting
free and open source, based on LLVM
icc icx Intel’s compilers, proprietary but now free
optimized for Intel hardware
icc being replaced by icx, based on LLVM
Visual C++ / MSVC Microsoft’s C++ compiler on Windows
Optimization
-g add debug symbols (also: -g3 or -ggdb)
-Ox 0 = no opt., 1-2 = opt., 3 = highly opt. (maybe
larger binary), g = opt. for debugging
Compilation environment
-I <path> where to find header files
-L <path> where to find libraries
-l <name> link with libname.so
-E / -c stop after preprocessing / compilation
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 444 / 548
Intro base OO More exp Tool conc py edit VCS format gcc web gdb sani valgrind static prof doxygen
> nm -C Struct.o
U strlen
U _Unwind_Resume
000000000000008a T SlowToCopy::SlowToCopy(SlowToCopy const&)
0000000000000000 T SlowToCopy::SlowToCopy()
0000000000000064 T SlowToCopy::SlowToCopy(std::__cxx11::basic_st
Makefiles
Why to use them
an organized way of describing building steps
avoids a lot of typing
Several implementations
raw Makefiles: suitable for small projects
cmake: portable, the current best choice
automake: GNU project solution
CMake
Example CMakeLists.txt
1 cmake_minimum_required(VERSION 3.18)
2 project(hello CXX)
3
CMake - Building
Compiler chain
Web tools
6 Useful tools
C++ editor
Version control
Code formatting
The Compiling Chain
Web tools
Debugging
Sanitizers
The Valgrind family
Static code analysis
Profiling
Doxygen
Typical usage
check small pieces of code on different compilers
check some new C++ functionality and its support
optimize small pieces of code
NOT relevant for large codes
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 452 / 548
Intro base OO More exp Tool conc py edit VCS format gcc web gdb sani valgrind static prof doxygen
Godbolt by example
Check effect of optimization flags
https://godbolt.org/z/Pb8WsWjEx
Check generated code with -O0, -O1, -O2, -O3
See how it gets shorter and simpler
cppinsights
Concept
Reveals the actual code behind C++ syntactic sugar
lambdas
range-based loops
templates
initializations
auto
...
Typical usage
understand how things work behind the C++ syntax
debug some non working pieces of code
cppinsights by example
Debugging
6 Useful tools
C++ editor
Version control
Code formatting
The Compiling Chain
Web tools
Debugging
Sanitizers
The Valgrind family
Static code analysis
Profiling
Doxygen
Debugging
The problem
everything compiles fine (no warning)
but crashes at run time
no error message, no clue
Debugging
The problem
everything compiles fine (no warning)
but crashes at run time
no error message, no clue
Debugging
The problem
everything compiles fine (no warning)
but crashes at run time
no error message, no clue
Existing tools
gdb THE main player
lldb the debugger coming with clang/LLVM
gdb-oneapi the Intel OneAPI debugger
inspect state
bt prints a backtrace
print <var> prints current content of the variable
list show code around current point
up/down go up or down in call stack
breakpoints
break <function> puts a breakpoint on function entry
break <file>:<line> puts a breakpoint on that line
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 458 / 548
Intro base OO More exp Tool conc py edit VCS format gcc web gdb sani valgrind static prof doxygen
gdb
Exercise: gdb
go to exercises/debug
compile, run, see the crash
run it in gdb (or lldb on newer MacOS)
inspect backtrace, variables
find problem and fix bug
try stepping, breakpoints
Debugging UIs
Sanitizers
6 Useful tools
C++ editor
Version control
Code formatting
The Compiling Chain
Web tools
Debugging
Sanitizers
The Valgrind family
Static code analysis
Profiling
Doxygen
Wrap up
If a program crashes, run it with asan
Should be part of every C++ continuous integration system
It will also find bugs that by luck didn’t crash the program
It doesn’t generate false positives
More info
https://github.com/google/sanitizers/wiki/
AddressSanitizer
Compile with asan, and start executable using
ASAN_OPTIONS=help=1 <executable>
% ./a.out
WARNING: ThreadSanitizer: data race (pid=19219)
Write of size 4 at 0x7fcf47b21bc0 by thread T1:
#0 Thread1 datarace.c:4 (exe+0x00000000a360)
https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 469 / 548
Intro base OO More exp Tool conc py edit VCS format gcc web gdb sani valgrind static prof doxygen
UBSan
Tracks uninitialised memory, broken arithmetic, wrong array
indexing and other undefined behaviour
Recompile your program with e.g.
clang++ -fsanitize=undefined -g -O1 ub.cpp
% ./a.out
up.cpp:3:5: runtime error: signed integer overflow:
2147483647 + 1 cannot be represented in type 'int'
https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
6 Useful tools
C++ editor
Version control
Code formatting
The Compiling Chain
Web tools
Debugging
Sanitizers
The Valgrind family
Static code analysis
Profiling
Doxygen
Valgrind fundamentals
valgrind is a framework for different tools
a processor simulator allowing checks in between instructions
slow (10-50 times slower than normal execution)
easy to use : “valgrind <your executable>”
no recompilation
better with -g -O0, but not strictly needed
it is free and open source
Valgrind fundamentals
valgrind is a framework for different tools
a processor simulator allowing checks in between instructions
slow (10-50 times slower than normal execution)
easy to use : “valgrind <your executable>”
no recompilation
better with -g -O0, but not strictly needed
it is free and open source
Main tools
memcheck a memory checker (default tool) and leak detector
callgrind a call graph builder
helgrind a race condition detector
memcheck
valgrind
Exercise: valgrind
go to exercises/valgrind
compile, run, it should work
run with valgrind, see the problem
fix the problem
memcheck
Exercise: memcheck
go to exercises/memcheck
compile, run, it should work
run with valgrind, see LEAK summary
run with --leak-check=full to get details
analyze and correct it
callgrind
keeps track of all function calls
and time spent in each function
build statistics on calls, CPU usages and more
outputs flat statistics file, quite unreadable
kcachegrind
a gui exploiting statistics built by callgrind
able to browse graphically the program calls
able to “graph” CPU usage on the program structure
callgrind
Exercise: callgrind
go to exercises/callgrind
compile, run, it will be slow
change nb iterations to 20
run with valgrind --tool=callgrind
look at output with kcachegrind
change fibo call to fibo2
observe the change in kcachegrind
helgrind
helgrind
helgrind
Exercise: helgrind
go to exercises/helgrind
compile, run
check it with valgrind. You may see strange behavior
or it will be perfectly fine
check it with valgrind --tool=helgrind
understand issue and fix
6 Useful tools
C++ editor
Version control
Code formatting
The Compiling Chain
Web tools
Debugging
Sanitizers
The Valgrind family
Static code analysis
Profiling
Doxygen
Static analysis
The problem
all the tools discussed so far work on binaries
they analyze the code being run
so there is a coverage problem (e.g. for error cases)
Static analysis
The problem
all the tools discussed so far work on binaries
they analyze the code being run
so there is a coverage problem (e.g. for error cases)
Static analysis
The problem
all the tools discussed so far work on binaries
they analyze the code being run
so there is a coverage problem (e.g. for error cases)
Existing tools
Coverity proprietary tool, the most complete
cppcheck free and opensource, but less complete
clang-tidy clang-based “linter”, includes clang static analyzer
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 481 / 548
Intro base OO More exp Tool conc py edit VCS format gcc web gdb sani valgrind static prof doxygen
cppcheck
Exercise: cppcheck
go to exercises/cppcheck
compile, run, see that it works
use valgrind: no issue
use cppcheck, see the problem
analyze the issue, and fix it
bonus: understand why valgrind did not complain
and how the standard deviation could be biased
hint : use gdb and check addresses of v and diffs
clang-tidy
Documentation and supported checks
https://clang.llvm.org/extra/clang-tidy/
Run clang-tidy
clang-tidy <file.cpp> -checks=...
clang-tidy <file.cpp> (checks from .clang-tidy file)
clang-tidy <file.cpp> --fix (applies fixes)
Compilation flags
clang-tidy needs to know exactly how your program is built
clang-tidy ... -- <all compiler flags>
.clang-tidy file
describes which checks to run
usually checked in at repository root
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 483 / 548
Intro base OO More exp Tool conc py edit VCS format gcc web gdb sani valgrind static prof doxygen
clang-tidy
Exercise: clang-tidy
go to any example which compiles (e.g.
exercises/cppcheck)
mkdir build && cd build
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..
clang-tidy <../file.cpp> -checks=*
inspect output
run with --fix flag
revert changes using git checkout <../file.cpp>
Profiling
6 Useful tools
C++ editor
Version control
Code formatting
The Compiling Chain
Web tools
Debugging
Sanitizers
The Valgrind family
Static code analysis
Profiling
Doxygen
Profiling
Conceptually
take a measurement of a performance aspect of a program
where in my code is most of the time spent?
is my program compute or memory bound?
does my program make good use of the cache?
is my program using all cores most of the time?
how often are threads blocked and why?
which API calls are made and in which order?
...
the goal is to find performance bottlenecks
is usually done on a compiled program, not on source code
perf
perf is a powerful command line profiling tool for linux
compile with -g -fno-omit-frame-pointer
perf stat -d <prg> gathers performance statistics while
running <prg>
perf record -g <prg> starts profiling <prg>
perf report displays a report from the last profile
More information in this wiki, this website or this talk.
Doxygen
6 Useful tools
C++ editor
Version control
Code formatting
The Compiling Chain
Web tools
Debugging
Sanitizers
The Valgrind family
Static code analysis
Profiling
Doxygen
Doxygen
Doxygen
Generates documentation from source code comments
Output formats: HTML, LaTeX, XML, ...
May be input for further generators, e.g. Sphinx
Doxygen uses a config file, usually called Doxyfile
Run doxygen -g to generate an initial Doxyfile
Edit Doxyfile (e.g. output format, source code location, etc.)
Run doxygen to (re-)generate documentation
View e.g. HTML documentation using a standard web browser
More on the doxygen website
Doxygen
Comment blocks
1 /** 10 ///
2 * text/directives ... 11 /// text/directives ...
3 */ 12 ///
4 some_cpp_entity 13 some_cpp_entity
5 14
6 /*! 15 //!
7 * text/directives ... 16 //! text/directives ...
8 */ 17 //!
9 some_cpp_entity 18 some_cpp_entity
More details available here
Doxygen
Comment directives
1 /**
2 * Long description here. May include HTML etc.
3 *
4 * \brief Checks whether i is odd
5 * \tparam Integral The integral type of the input
6 * \param i Input value
7 * \return True if i is odd, otherwise false
8 * \throw std::out_of_range if i is larger than 100
9 * \see isEven
10 */
11 template <typename Integral>
12 bool isOdd(Integral i);
All directives can also start with @ instead of \
A list of all commands can be found here
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 492 / 548
Intro base OO More exp Tool conc py thr mutex atomic TLS condition
Concurrency
6 Useful tools
1 History and goals
7 Concurrency
Threads and async
2 Language basics
Mutexes
Atomic types
3 Object orientation (OO) Thread-local storage
Condition Variables
4 Core modern C++
8 C++ and python
5 Expert C++
7 Concurrency
Threads and async
Mutexes
Atomic types
Thread-local storage
Condition Variables
Example code
1 void foo() {...}
2 void bar() {...}
3 int main() {
4 std::thread t1{foo};
5 std::thread t2{bar};
6 for (auto t: {&t1,&t2}) t->join();
7 return 0;
8 }
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 495 / 548
Intro base OO More exp Tool conc py thr mutex atomic TLS condition
Concept
separation of the specification of what should be done and the
retrieval of the results
“start working on this, and let me check if it’s done”
Concept
separation of the specification of what should be done and the
retrieval of the results
“start working on this, and let me check if it’s done”
Practically
std::async function launches an asynchronous task
std::future<T> allows to retrieve the result
Concept
separation of the specification of what should be done and the
retrieval of the results
“start working on this, and let me check if it’s done”
Practically
std::async function launches an asynchronous task
std::future<T> allows to retrieve the result
Example code
1 int f() {...}
2 std::future<int> res = std::async(f);
3 std::cout << res.get() << "\n";
Usage
1 int f() {...}
2 auto res1 = std::async(std::launch::async, f);
3 auto res2 = std::async(std::launch::deferred, f);
std::packaged_task template
creates an asynchronous version of any function-like object
identical arguments
returns a std::future
provides access to the returned future
associated with threads, gives full control on execution
std::packaged_task template
creates an asynchronous version of any function-like object
identical arguments
returns a std::future
provides access to the returned future
associated with threads, gives full control on execution
Usage
1 int f() { return 42; }
2 std::packaged_task<int()> task{f};
3 auto future = task.get_future();
4 task();
5 std::cout << future.get() << std::endl;
Mutexes
7 Concurrency
Threads and async
Mutexes
Atomic types
Thread-local storage
Condition Variables
Races C++ 11
Example code - godbolt
1 int a = 0;
2 void inc() { a++; };
3 void inc100() {
4 for (int i=0; i < 100; i++) inc();
5 };
6 int main() {
7 std::thread t1{inc100};
8 std::thread t2{inc100};
9 for (auto t: {&t1,&t2}) t->join();
10 std::cout << a << "\n";
11 }
Races C++ 11
Example code - godbolt
1 int a = 0;
2 void inc() { a++; };
3 void inc100() {
4 for (int i=0; i < 100; i++) inc();
5 };
6 int main() {
7 std::thread t1{inc100};
8 std::thread t2{inc100};
9 for (auto t: {&t1,&t2}) t->join();
10 std::cout << a << "\n";
11 }
Races C++ 11
Example code - godbolt
1 int a = 0;
2 void inc() { a++; };
3 void inc100() {
4 for (int i=0; i < 100; i++) inc();
5 };
6 int main() {
7 std::thread t1{inc100};
8 std::thread t2{inc100};
9 for (auto t: {&t1,&t2}) t->join();
10 std::cout << a << "\n";
11 }
Atomicity C++ 11
Definition (wikipedia)
an operation (or set of operations) is atomic if it appears to
the rest of the system to occur instantaneously
Practically
an operation that won’t be interrupted by other concurrent
operations
an operation that will have a stable environment during
execution
Atomicity C++ 11
Definition (wikipedia)
an operation (or set of operations) is atomic if it appears to
the rest of the system to occur instantaneously
Practically
an operation that won’t be interrupted by other concurrent
operations
an operation that will have a stable environment during
execution
Is ++ operator atomic ?
Atomicity C++ 11
Definition (wikipedia)
an operation (or set of operations) is atomic if it appears to
the rest of the system to occur instantaneously
Practically
an operation that won’t be interrupted by other concurrent
operations
an operation that will have a stable environment during
execution
Is ++ operator atomic ?
Usually not. It behaves like :
1 eax = a // memory to register copy
2 increase eax // increase (atomic CPU instruction)
3 a = eax // copy back to memory
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 502 / 548
Intro base OO More exp Tool conc py thr mutex atomic TLS condition
Timing C++ 11
Code
1 eax = a // memory to register copy
2 increase eax // increase (atomic CPU instruction)
3 a = eax // copy back to memory
For 2 threads
Thread 1:eax Memory:a Thread 2:eax
read read
0 0
incr incr
time
write 1 write 1
The objects
std::mutex in the mutex header. Mutual exclusion
std::scoped_lock RAII to lock and unlock automatically
std::unique_lock same, but can be released/relocked explicitly
The objects
std::mutex in the mutex header. Mutual exclusion
std::scoped_lock RAII to lock and unlock automatically
std::unique_lock same, but can be released/relocked explicitly
Practically - godbolt
1 int a = 0;
2 std::mutex m;
3 void inc() {
4 std::scoped_lock lock{m};
5 a++;
6 }
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 504 / 548
Intro base OO More exp Tool conc py thr mutex atomic TLS condition
1 void function(...) {
2 // uncritical work ...
3 {
4 std::scoped_lock myLocks{mutex1, mutex2, ...};
5 // critical section
6 }
7 // uncritical work ...
8 }
Sequence diagram
Thread 1: Mutex A: Mutex B: Thread 2:
lock
lock
lock (block)
Possible solutions
C++ 17: std::scoped_lock lock{m1, m2}; comes with
deadlock-avoidance algorithm
Never take several locks
Or add master lock protecting the locking phase
Respect a strict locking order across all threads
Do not use locks
Use other techniques, e.g. lock-free queues
8 std::thread writer([&](){
9 std::scoped_lock lock{mutex}; // exclusive
10 modify(data); // Only one can write
11 });
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 509 / 548
Intro base OO More exp Tool conc py thr mutex atomic TLS condition
Atomic types
7 Concurrency
Threads and async
Mutexes
Atomic types
Thread-local storage
Condition Variables
std::atomic template
Any trivially copyable type can be made atomic in C++
Most useful for integral types
May internally use locks for custom types
1 std::atomic<int> a{0};
2 std::thread t1([&](){ a++; });
3 std::thread t2([&](){ a++; });
4 a += 2;
5 t1.join(); t2.join();
6 assert( a == 4 ); // Guaranteed to succeed
Sequence diagram
Thread 1: atomic: Thread 2:
load load
+2 +2
time
store 2 store 2
1 std::atomic<int> a{0};
2 std::thread t1([&]{ a.fetch_add(2); });
3 std::thread t2([&]{ a += 2; });
Sequence diagram
Thread 1: atomic: Thread 2:
fetch_add
time
+=
1 int a{0};
2 std::thread t1([&]{ std::atomic_ref<int> r{a}; r++;});
3 std::thread t2([&]{ std::atomic_ref{a}++; });
4 t1.join(); t2.join();
5 a += 2; // non-atomic (fine, threads joined before)
6 assert( a == 4 ); // Guaranteed to succeed
Exercise: Atomics
Go to exercises/atomic
You’ll find a program with the same race condition as in race
Fix it using std::atomic
Thread-local storage
7 Concurrency
Threads and async
Mutexes
Atomic types
Thread-local storage
Condition Variables
thread_local keyword
A variable can be declared thread-local
Then every thread will have its own copy
Most useful for “working memory” in each thread
Note: Can also be declared directly on the stack of a thread
and will be faster, thus should be preferred when possible
Condition Variables
7 Concurrency
Threads and async
Mutexes
Atomic types
Thread-local storage
Condition Variables
Usage
Use RAII-style locks to protect shared data
wait() will block until the condition is met
you can have several waiters sharing the same mutex
notify_one() will wake up one waiter
notify_all() will wake up all waiters
1 std::mutex mutex;
2 std::condition_variable cond;
3 Data data;
4 std::thread producer([&](){
5 {
6 std::scoped_lock lock{mutex};
7 data = produceData(); // may take long ...
8 }
9 cond.notify_all();
10 });
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 520 / 548
Intro base OO More exp Tool conc py thr mutex atomic TLS condition
Naïve waiting
1 auto processData = [&](){
2 std::unique_lock<std::mutex> lock{mutex};
3 cond.wait(lock, [&](){ return data.isReady(); });
4 process(data);
5 };
Naïve waiting
1 auto processData = [&](){
2 std::unique_lock<std::mutex> lock{mutex};
3 cond.wait(lock, [&](){ return data.isReady(); });
4 process(data);
5 };
Correct waiting
1 auto processData = [&](){
2 {
3 std::unique_lock<std::mutex> lock{mutex};
4 cond.wait(lock, [&](){ return data.isReady(); });
5 }
6 process(data);
7 };
8 std::thread t1{processData}, t2{processData}, ...;
9 for (auto t : {&producer, &t1, &t2, ...}) t->join();
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 522 / 548
Intro base OO More exp Tool conc py thr mutex atomic TLS condition
Condition variables
6 Useful tools
1 History and goals
7 Concurrency
2 Language basics
8 C++ and python
Writing a module
3 Object orientation (OO)
Marrying C++ and C
The ctypes module
4 Core modern C++ The cppyy project
5 Expert C++
Writing a module
A question of mangling
Mangling
the act of converting the name of variable or function to a symbol
name in the binary code
C mangling
Source : file.c
1 float sum(float a, float b);
2 int square(int a);
3 // won't compile : conflicting types for ‘square’
4 // float square(float a);
Source : file.cpp
1 float sum(float a, float b);
2 int square(int a);
3 // ok, signature is different
4 float square(float a);
extern “C”
These functions will use C mangling :
1 extern "C" {
2 float sum(float a, float b);
3 int square(int a);
4 }
extern “C”
These functions will use C mangling :
1 extern "C" {
2 float sum(float a, float b);
3 int square(int a);
4 }
extern “C”
These functions will use C mangling :
1 extern "C" {
2 float sum(float a, float b);
3 int square(int a);
4 }
How it works
uses Just in Time compilation through cling
an interactive C++ interpreter
Questions ?
https://github.com/hsf-training/cpluspluscourse/raw/download/talk/C++Course_full.pdf
https://github.com/hsf-training/cpluspluscourse
Index of Exercises
Books
A Tour of C++, Third Edition
Bjarne Stroustrup, Addison-Wesley, Sep 2022
ISBN-13: 978-0136816485
Effective Modern C++
Scott Meyers, O’Reilly Media, Nov 2014
ISBN-13: 978-1-491-90399-5
C++ Templates - The Complete Guide, 2nd Edition
David Vandevoorde, Nicolai M. Josuttis, and Douglas Gregor
ISBN-13: 978-0-321-71412-1
C++ Best Practices, 2nd Edition
Jason Turner
https://leanpub.com/cppbestpractices
Clean Architecture
Robert C. Martin, Pearson, Sep 2017
ISBN-13: 978-0-13-449416-6
The Art of UNIX Programming
Eric S. Raymond, Addison-Wesley, Sep 2002
ISBN-13: 978-0131429017
Introduction to Algorithms, 4th Edition
T. H. Cormen, C. E. Leiserson, R. L. Rivest, C. Stein, Apr 2022
ISBN-13: 978-0262046305
B. Gruber, S. Hageboeck, S. Ponce HEP C++ course 547 / 548
Index External Resources and Further Reading
Conferences