From 7bdd8eada752746129f65c2745d7fff10c1cc097 Mon Sep 17 00:00:00 2001 From: Stephan Hageboeck Date: Fri, 20 Jun 2025 10:50:27 +0200 Subject: [PATCH 1/3] Add a slide on vector move pessimisation. Underline why move constructors must be noexcept using the example of a vector that needs to reallocate. --- talk/morelanguage/move.tex | 88 +++++++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) diff --git a/talk/morelanguage/move.tex b/talk/morelanguage/move.tex index 038c7baa..828f5062 100644 --- a/talk/morelanguage/move.tex +++ b/talk/morelanguage/move.tex @@ -134,6 +134,66 @@ \end{exampleblock} \end{frame} +\begin{frame}[fragile] + \frametitlecpp[11]{Move semantics: Value categories} + \begin{block}{rvalues} + \begin{itemize} + \item Either ``pure rvalue'' + \begin{itemize} + \item result of a built-in operator + \item initalizer of an object + \end{itemize} + \item Or an expiring value (resources can be reused) + \item Cannot be used as left-hand operand of assignment operators + \item Cannot take its address + \item rvalues bind to \cppinline{func(T&&)} + \end{itemize} + \end{block} + \vspace{-6mm} + \begin{overprint} + \onslide<1> + \begin{exampleblock}{Examples for rvalues} + \begin{cppcode*}{} + T t = T{}; // T{} is pure rv + 42; // Pure rv + a + b; // Pure rv + i++; // Pure rv (post-increment) + T{}.m; // m is expiring because T{} is pure rv + std::move(x); // Expiring, because returns T&& + \end{cppcode*} + \end{exampleblock} + \onslide<2> + \begin{alertblock}{These don't compile} + \begin{cppcode*}{} + a + b = 2; // Cannot assign to rvalues + &i++; // Cannot take address of rvalues + T{}.m = 2; // Member of pure rvalue is expiring value + T& t = std::move(x); // Cannot bind reference to expiring value + \end{cppcode*} + \end{alertblock} + \end{overprint} +\end{frame} + +\begin{frame}[fragile] + \frametitlecpp[11]{Move semantics: Value categories} + \begin{block}{lvalues} + \begin{itemize} + \item Expression whose evaluation determines identity of object or function + \item And which is not an expiring value + \item lvalues don't bind to \cppinline{func(T&&)} + \end{itemize} + \end{block} + \begin{exampleblock}{lvalue examples} + \begin{cppcode*}{} + int i; + int &j = i; + T t; + t.m = 5; // lvalue, because t is lvalue + ++i; // Pre-increment is lvalue + \end{cppcode*} + \end{exampleblock} +\end{frame} + \begin{frame}[fragile] \frametitlecpp[11]{Move semantics} \begin{block}{A few points} @@ -214,7 +274,7 @@ \item 1 swap less, separate copy assignment operator needed \item former content of \cppinline{*this} destroyed with caller argument \end{itemize} - \item swap, move constructor/assignment must be \cppinline{noexcept} + \item swap, move constructor/assignment must be \cppinline{noexcept}! \end{itemize} \end{block} \end{frame} @@ -267,6 +327,32 @@ \end{exampleblock} \end{frame} +\begin{frame}[fragile,t] + \frametitlecpp[11]{Move semantics: Don't forget noexcept!} + \begin{alertblock}{Vector pessimisation when move constructor can throw} + \begin{itemize} + \item When a vector reallocates, it must copy/move all elements + \begin{itemize} + \item An exception during move aborts the relocation of elements + \item Move might not be revertible, so vector falls back to copy + \item This can significantly slow down the resize operation + \end{itemize} + \item With \cppinline{noexcept}, the much faster move is used + \end{itemize} + \end{alertblock} + \begin{exampleblock}{Vector resize with copy/move} + \small + \begin{cppcode*}{} + struct Movable1 { Movable1(Movable1 &&other); }; + struct Movable2 { Movable2(Movable2 &&other) noexcept; }; + while (vector1.size() < 10000) { + vector1.push_back(Movable1{}); // Copies + vector2.push_back(Movable2{}); // Moves + } + \end{cppcode*} + \end{exampleblock} +\end{frame} + \begin{frame}[fragile] \frametitlecpp[11]{Move Semantic} \begin{exercise}{Move semantics} From 078bb6417a82b8a2ac7a9b633bea21353a039030 Mon Sep 17 00:00:00 2001 From: Stephan Hageboeck Date: Wed, 20 Aug 2025 16:53:27 +0200 Subject: [PATCH 2/3] Add slides on value categories. Fix #522. --- talk/morelanguage/move.tex | 165 ++++++++++++++++++++++++++----------- 1 file changed, 118 insertions(+), 47 deletions(-) diff --git a/talk/morelanguage/move.tex b/talk/morelanguage/move.tex index 828f5062..7d0f659f 100644 --- a/talk/morelanguage/move.tex +++ b/talk/morelanguage/move.tex @@ -108,45 +108,44 @@ \end{frame} \begin{frame}[fragile] - \frametitlecpp[11]{Move semantics} - \begin{block}{The idea} + \frametitlecpp[11]{Move semantics: value categories} + \begin{block}{Lvalue (left value / locator value)} \begin{itemize} - \item a new type of reference: rvalue reference + \item An expression is an lvalue \begin{itemize} - \item used for move semantic - \item denoted by \cppinline{&&} + \item If its evaluation determines identity of object/function + \item If the object is not expiring (``xvalue'') \end{itemize} - \item 2 new special member functions in every class: - \begin{description} - \item[a move constructor] similar to copy constructor - \item[a move assignment operator] similar to assignment operator (now called copy assignment operator) - \end{description} + \item Lvalues have a persistent address in memory + \item Lvalues can be assigned to unless \cppinline{const} \end{itemize} \end{block} - \pause - \begin{exampleblock}{Practically} + \begin{exampleblock}{Lvalue examples} \begin{cppcode*}{} - T(T const & other); // copy construction - T( T&& other); // move construction - T& operator=(T const & other); // copy assignment - T& operator=( T&& other); // move assignment + int i; + int & j = i; + T t; + t.m = 5; // lvalue, because t is lvalue + ++i; // returns underlying object \end{cppcode*} \end{exampleblock} \end{frame} \begin{frame}[fragile] - \frametitlecpp[11]{Move semantics: Value categories} - \begin{block}{rvalues} + \frametitlecpp[11]{Move semantics: value categories} + \begin{block}{Rvalue} \begin{itemize} - \item Either ``pure rvalue'' + \item An expression is an rvalue when from these primary categories \begin{itemize} - \item result of a built-in operator - \item initalizer of an object + \item ``pure rvalue'': Result of built-in operator; initialiser of object + \item ``xvalue'': Expiring value, resources can ``moved'' \end{itemize} - \item Or an expiring value (resources can be reused) - \item Cannot be used as left-hand operand of assignment operators - \item Cannot take its address - \item rvalues bind to \cppinline{func(T&&)} + \item Has no persistent address in memory + \begin{itemize} + \item Cannot use the addressof operator + \end{itemize} + \item Cannot be used as left-hand operand of built-in assignments + \end{itemize} \end{block} \vspace{-6mm} @@ -154,42 +153,114 @@ \onslide<1> \begin{exampleblock}{Examples for rvalues} \begin{cppcode*}{} + i = 42; // 42 is pure rv + i = a + b; // a+b is pure rv T t = T{}; // T{} is pure rv - 42; // Pure rv - a + b; // Pure rv - i++; // Pure rv (post-increment) + i++; // Pure rv (post-increment returns temp.) T{}.m; // m is expiring because T{} is pure rv - std::move(x); // Expiring, because returns T&& + std::move(x); // Converts lvalue into xvalue \end{cppcode*} \end{exampleblock} \onslide<2> - \begin{alertblock}{These don't compile} + \begin{alertblock}{Things that don't compile with rvalues} \begin{cppcode*}{} - a + b = 2; // Cannot assign to rvalues - &i++; // Cannot take address of rvalues - T{}.m = 2; // Member of pure rvalue is expiring value - T& t = std::move(x); // Cannot bind reference to expiring value + a + b = 2; // Cannot assign to pure rv + &(a+b); // Cannot take address of pure rv + &i++; // Same, post-increment returns pure rv + T{}.m = 2; // cannot assign if m is built-in type \end{cppcode*} \end{alertblock} \end{overprint} \end{frame} \begin{frame}[fragile] - \frametitlecpp[11]{Move semantics: Value categories} - \begin{block}{lvalues} + \frametitlecpp[11]{Move semantics: Terminology} + \begin{block}{Source of confusion} \begin{itemize} - \item Expression whose evaluation determines identity of object or function - \item And which is not an expiring value - \item lvalues don't bind to \cppinline{func(T&&)} + \item The name ``rvalue'' originates from them \textit{mostly} being on right-hand side + \item But it denotes value categories of \emph{expressions} + \item Rvalues can be assigned to for non-builtin types \end{itemize} \end{block} - \begin{exampleblock}{lvalue examples} + + \begin{exampleblock}{Example: rvalue on left-hand side} \begin{cppcode*}{} - int i; - int &j = i; - T t; - t.m = 5; // lvalue, because t is lvalue - ++i; // Pre-increment is lvalue + struct T{ + int m; + T& operator=(int i); + }; + std::cout << (T{} = 5).m << "\n"; + \end{cppcode*} + \end{exampleblock} + + \begin{alertblock}{Not possible with built-in types} + \begin{cppcode*}{} + std::cout << (int{} = 5) << "\n"; // Error + \end{cppcode*} + \end{alertblock} +\end{frame} + +\begin{frame}[fragile] + \frametitlecpp[11]{Move semantics: rvalue references} + \begin{block}{New reference type: rvalue reference} + \begin{itemize} + \item Declared with \cppinline{T&&} + \item Does not bind to lvalues + \end{itemize} + \end{block} + + \begin{exampleblock}{Binding rules} + \begin{columns}[onlytextwidth] + \begin{column}{0.45\textwidth} + Lvalues + \begin{cppcode*}{linenos=false} + T lv; + T & ref = lv;//OK + T && rvref = lv;//Error + T const& cr = lv;//OK + \end{cppcode*} + \end{column} + \hfil + \begin{column}{0.5\textwidth} + Rvalues + \begin{cppcode*}{linenos=false} + + T & ref = T{};//Error + T && rvref = T{};//OK + T const& cr = T{};//OK + \end{cppcode*} + \end{column} + \end{columns} + \end{exampleblock} + + \begin{exampleblock}{Overload resolution prefers rvalue reference} + \begin{cppcode*}{} + void f(T const &); + void f(T &&); // Selected. Enables move + f(T{}); + \end{cppcode*} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile] + \frametitlecpp[11]{Move semantics} + \begin{block}{The idea} + \begin{itemize} + \item use rvalue references to reuse resources + \item 2 new special member functions in every class: + \begin{description} + \item[a move constructor] similar to copy constructor + \item[a move assignment operator] similar to assignment operator (now called copy assignment operator) + \end{description} + \end{itemize} + \end{block} + \pause + \begin{exampleblock}{Practically} + \begin{cppcode*}{} + T(T const & other); // copy construction + T( T&& other); // move construction + T& operator=(T const & other); // copy assignment + T& operator=( T&& other); // move assignment \end{cppcode*} \end{exampleblock} \end{frame} @@ -205,14 +276,14 @@ \end{itemize} \item if no move semantic is implemented, copies will be performed \item the language and STL understand move semantic - \item the compiler moves whenever possible + \item the compiler uses copy elision or moves whenever possible \begin{itemize} \item e.g.\ when passing temporaries or returning from a function \end{itemize} \end{itemize} \end{block} \pause - \begin{exampleblock}{Practically} + \begin{exampleblock}{Practically (in \cpp11)} \begin{cppcode*}{} T f() { T r; return r; } // move r out of f T v = f(); // move returned (temporary) T into v From c653d29a3aadd58a5211409ebd718d637969e7af Mon Sep 17 00:00:00 2001 From: Stephan Hageboeck Date: Thu, 9 Oct 2025 15:09:31 +0200 Subject: [PATCH 3/3] Remove colour box from & characters (or escape them). When colour or other formatting is used in minted with "escapeinside", "&" needs to be escaped depending on the package versions. For one instance, the colour box isn't deemed necessary, any more, so it was removed. --- talk/basicconcepts/functions.tex | 2 +- talk/morelanguage/raii.tex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/talk/basicconcepts/functions.tex b/talk/basicconcepts/functions.tex index 6ef973f5..a3d26376 100644 --- a/talk/basicconcepts/functions.tex +++ b/talk/basicconcepts/functions.tex @@ -341,7 +341,7 @@ struct T {...}; T a; void fVal(T value); fVal(a); // by value void fRef(const T &value); fRef(a); // by reference -void fPtr(const T *value); fPtr(|{\setlength{\fboxsep}{0pt}\color{gray}\colorbox{yellow}{\textsc{&}}}|a); // by pointer +void fPtr(const T *value); fPtr(&a); // by pointer void fWrite(T &value); fWrite(a); // non-const ref \end{cppcode*} \end{block} diff --git a/talk/morelanguage/raii.tex b/talk/morelanguage/raii.tex index b895fe07..3722cb85 100644 --- a/talk/morelanguage/raii.tex +++ b/talk/morelanguage/raii.tex @@ -370,7 +370,7 @@ % escapeinside seems to break gobble, so need to un-indent manually \begin{cppcode*}{escapeinside=@@} auto shared = std::make_shared(100); -auto print = [@\textcolor{red}{&}@shared](){ +auto print = [@\textcolor{red}{\&}@shared](){ std::cout << "Use: " << shared.use_count() << " " << "value: " << *shared << "\n"; };