diff --git a/notebooks/14_MoveSemantics+RuleOfSix.ipynb b/notebooks/14_MoveSemantics+RuleOfSix.ipynb
index 3bed6a6fab7aaa3e9007ce3fc8f039d86e50046a..028b8dc5129d16839899fb4ff289cf313f2bd813 100644
--- a/notebooks/14_MoveSemantics+RuleOfSix.ipynb
+++ b/notebooks/14_MoveSemantics+RuleOfSix.ipynb
@@ -1 +1,630 @@
-{"metadata":{"kernelspec":{"display_name":"C++17","language":"C++17","name":"xcpp17"},"language_info":{"codemirror_mode":"text/x-c++src","file_extension":".cpp","mimetype":"text/x-c++src","name":"c++","version":"17"}},"nbformat_minor":5,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Move Semantics","metadata":{},"id":"4c244310-2195-4b50-a923-df0b9ba15f7d"},{"cell_type":"markdown","source":"In order to understand what this is about let us take a look at an example from Rainer Grimm's blog [\"Modernes C++ in der Praxis\"](https://www.linux-magazin.de/ausgaben/2012/12/c-11/):","metadata":{},"id":"92b6cf27-b829-4efd-9395-e38f3a330bb4"},{"cell_type":"code","source":"#include <algorithm>\n#include <iostream>\n#include <vector>\n\nclass BigArrayCopy {\n\npublic:\n\n  BigArrayCopy( size_t len ) : len_( len ), data_( new int[ len ] ) {}\n\n  BigArrayCopy( const BigArrayCopy& other ) : len_( other.len_ ),\n                                              data_( new int[ other.len_ ] ) {\n    std::cout << \"copy construction of \" << other.len_ << \" elements\" << std::endl;\n    std::copy( other.data_, other.data_ + len_, data_ );\n  }\n\n  BigArrayCopy& operator=( const BigArrayCopy& other ) {\n\n    std::cout << \"copy assignment of \" << other.len_ << \" elements\" << std::endl;\n\n    if( this != &other ) {\n\n      delete[] data_;\n      len_ = other.len_;\n      data_ = new int[ len_ ];\n\n      std::copy( other.data_, other.data_ + len_, data_ );\n\n    }\n\n    return *this;\n  }\n\n  ~BigArrayCopy() {\n    if( data_ != nullptr ) {\n      delete[] data_;\n    }\n  }\nprivate:\n\n  size_t len_;\n  int* data_;\n\n};","metadata":{"trusted":true},"execution_count":2,"outputs":[],"id":"4ffa9b8d-02be-4919-882c-18556a6ded7d"},{"cell_type":"code","source":"int main() {\n    \n  std::vector< BigArrayCopy > myVec;\n\n  BigArrayCopy bArray( 11111111 );\n  BigArrayCopy bArray2( bArray );\n\n  myVec.push_back( bArray );\n  bArray = BigArrayCopy( 22222222 );\n  myVec.push_back( BigArrayCopy( 33333333 ) );\n\n}\n\nmain();","metadata":{"trusted":true},"execution_count":3,"outputs":[{"name":"stdout","output_type":"stream","text":"copy construction of 11111111 elements\ncopy construction of 11111111 elements\ncopy assignment of 22222222 elements\ncopy construction of 33333333 elements\ncopy construction of 11111111 elements\n"}],"id":"dd1e7485-7b8e-4728-a217-df899e07d6d8"},{"cell_type":"markdown","source":"Let us analyse the output. Where do the five copies come from?\n<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>\n_____","metadata":{},"id":"333e1d6f-aab5-4c70-859d-db4dafb02981"},{"cell_type":"markdown","source":"We can change that by adding a **move constructor** and a **move assignment operator** to our class:","metadata":{},"id":"6893f7df-99e0-44e9-a79f-8c6e8acba110"},{"cell_type":"code","source":"#include <algorithm>\n#include <iostream>\n#include <vector>\n\nusing std::cout;\nusing std::endl;\nusing std::vector;\n\nclass BigArray {\n\npublic:\n\n  BigArray( size_t len ) : len_( len ), data_( new int[ len ] ) {}\n\n  BigArray( const BigArray& other ) : len_( other.len_ ),\n                                              data_( new int[ other.len_ ] ) {\n    cout << \"copy construction of \" << other.len_ << \" elements\" << endl;\n    std::copy( other.data_, other.data_ + len_, data_ );\n  }\n\n  BigArray& operator=( const BigArray& other ) {\n\n    cout << \"copy assignment of \" << other.len_ << \" elements\" << endl;\n\n    if( this != &other ) {\n\n      delete[] data_;\n      len_ = other.len_;\n      data_ = new int[ len_ ];\n\n      std::copy( other.data_, other.data_ + len_, data_ );\n\n    }\n\n    return *this;\n  }\n\n  BigArray( BigArray&& other ) : len_( other.len_ ),\n                                              data_( other.data_ ) {\n    cout << \"move construction of \" << other.len_ << \" elements\" << endl;\n    other.len_ = 0;\n    other.data_ = nullptr;\n  }\n\n  BigArray& operator=( BigArray&& other ) {\n\n    cout << \"move assignment of \" << other.len_ << \" elements\" << endl;\n\n    if( this != &other ) {\n\n      delete[] data_;\n\n      len_ = other.len_;\n      data_ = other.data_;\n\n      other.len_ = 0;\n      other.data_ = nullptr;\n    }\n\n    return *this;\n  }\n\n  ~BigArray() {\n    if( data_ != nullptr ) {\n      delete[] data_;\n    }\n  }\nprivate:\n\n  size_t len_;\n  int* data_;\n\n};","metadata":{"trusted":true},"execution_count":1,"outputs":[],"id":"e3784252-4e44-4260-a5a1-d2b92f92e264"},{"cell_type":"code","source":"int main() {\n    \n  std::vector< BigArray > myVec;\n  myVec.reserve(2); // get's rid of the final copy operation, when myVec was reallocated\n\n  BigArray bArray( 11111111 );\n  BigArray bArray2( bArray );\n\n  myVec.push_back( bArray );\n  bArray = BigArray( 22222222 );\n  myVec.push_back( BigArray( 33333333 ) );\n\n}\n\nmain();","metadata":{"trusted":true},"execution_count":4,"outputs":[{"name":"stdout","output_type":"stream","text":"copy construction of 11111111 elements\ncopy construction of 11111111 elements\nmove assignment of 22222222 elements\nmove construction of 33333333 elements\n"}],"id":"ebbe2374-3d00-4340-98a0-9352ff8ca2cb"},{"cell_type":"markdown","source":"As we can see the assignment and copying of the two temporaries in lines # 10 and 11 now uses our **move semantics**.\n\nBut what is does the **\"&&\"** in the interface of the two methods represent? It is an **r-value reference**.\n","metadata":{},"id":"2fead1ad-cee3-4a0d-ba70-b14066a349b9"},{"cell_type":"markdown","source":"### R-Value References\n\nFirst of all, what are \"r-values\"? Complicated question, short (simplified) answer: stuff that can appear on the right-hand side of an assignment, hence the name (originally). Examples of r-values are:\n\n- temporary objects\n- unnamed objects\n- objects whose address is undeterminable\n\nsuch as\n\n```\nint fourtyTwo = 42;\nstd::string a = std::string( \"rhs is an rvalue\");\nstd::string b = std::string( \"r\" ) +  std::string( \"-value\" );\nstd::string c = a + b;\nstd::string d = std::move(b);\n```\n\nThe last line is especially interesting. [std::move](https://en.cppreference.com/w/cpp/utility/move) effectively returns an r-value reference for its argument:\n\n\n> std::move is used to indicate that an object t may be \"moved from\", i.e. allowing the efficient transfer of resources from t to another object. In particular, std::move produces an xvalue expression that identifies its argument t. It is exactly equivalent to a static_cast to an rvalue reference type. ","metadata":{},"id":"a2f2682a-1823-46c4-a444-a23fc192ef17"},{"cell_type":"code","source":"int value = 1;\nint& lRef1 = value;\n// int&& rRef1 = value;\nint&& rRef2 = 1;\nconst int& lRef2 = 1;","metadata":{"trusted":true},"execution_count":7,"outputs":[],"id":"7c569151-43ef-44a8-babc-6928ca17b626"},{"cell_type":"markdown","source":"An r-value can bind to an r-value reference, but also to a constant l-value reference. That's why `BigArrayCopy` worked after all. However, binding to an r-value reference, if possible has **higher precedence**. That's what we need in `BigArray` for the move methods.","metadata":{},"id":"1ad6ad50-160f-49d9-b486-2ac84c1cbdbb"},{"cell_type":"markdown","source":"#### STL Containers","metadata":{},"id":"a0fd8007-04cc-43a5-8c29-07de525a132b"},{"cell_type":"markdown","source":"The containers of the STL support move semantics:","metadata":{},"id":"87780808-08d4-4463-8783-5b21114dc9f6"},{"cell_type":"code","source":"%%file std::move.cpp\n\n#include <iostream>\n#include <vector>\n#include <string>\n\nvoid show( const std::vector< std::string >& vector, const std::string& name ) {\n  std::cout << \"Length of vector '\" << name << \"': \" << vector.size()\n            << \", contents:\" << std::endl;\n  for( const auto& str: vector ) {\n    std::cout << str << std::endl;\n  }\n  std::cout << std::endl;\n}\n\nint main() {\n\n  std::vector< std::string > src{ \"Move\", \"Semantics\", \"in\", \"the\", \"STL\" };\n  show( src, \"src\" );\n\n  std::vector< std::string > dst{ std::move(src) };\n  show( dst, \"dst\" );\n  show( src, \"src\" );\n\n}","metadata":{"trusted":true},"execution_count":20,"outputs":[{"name":"stdout","text":"Overwriting std::move.cpp\n","output_type":"stream"}],"id":"8e2a7e79-bcd5-4207-a36e-72bcae38b187"},{"cell_type":"code","source":"!g++ std::move.cpp","metadata":{"trusted":true},"execution_count":21,"outputs":[],"id":"fb060696-86ce-440e-8193-5cf540ee6a32"},{"cell_type":"code","source":"!./a.out","metadata":{"trusted":true},"execution_count":22,"outputs":[{"name":"stdout","text":"Length of vector 'src': 5, contents:\nMove\nSemantics\nin\nthe\nSTL\n\nLength of vector 'dst': 5, contents:\nMove\nSemantics\nin\nthe\nSTL\n\nLength of vector 'src': 0, contents:\n\n","output_type":"stream"}],"id":"4712b2fd-4f11-4328-889c-bce3ed9c727e"},{"cell_type":"markdown","source":"Additionally the also support **`move iterators`**. See this nice example on [Fluent C++](https://www.fluentcpp.com/2017/04/25/move-iterators).","metadata":{},"id":"b8b8c25b-ac7c-4263-9dcc-5cc1ea0f5408"},{"cell_type":"markdown","source":"# Rule of Six / Five / Zero","metadata":{},"id":"aa620485-bd12-4727-9c7f-d07f66d0e62a"},{"cell_type":"markdown","source":"The advantage of *move semantics* is that it helps to avoid unnecessary copy operations in order to increase performance. However, it also means that a simple class now could implement the following *six different construction/destruction/assigment methods*:","metadata":{},"id":"6549163b-b1f8-4d7b-9126-0674f01d0022"},{"cell_type":"code","source":"class simple {\n    \n    // (1) Default Constructor\n    simple() {};\n    \n    // (2) Destructor\n    ~simple(){};\n    \n    // (3) Copy Constructor\n    simple( const simple& other ) {};\n    \n    // (4) Copy Assigment\n    simple& operator= ( const simple& other ) {\n        simple* aux = new( simple );\n        // copy stuff from other to aux\n        return *aux;\n    };\n    \n    // (5) Move Constructor\n    simple( const simple&& other ) {};\n    \n    // (6) Move Assignment\n    simple& operator= ( const simple&& other ) {\n        simple* aux = new( simple );\n        // move stuff from other to aux\n        // and potentially set other to 'zero'\n        return *aux;\n    };\n}","metadata":{"trusted":true},"execution_count":14,"outputs":[],"id":"381417d5-fe96-4249-a509-29fcfdd5615b"},{"cell_type":"markdown","source":"The question is, what happens, if we implement none or only some of these methods? For example we know that a default constructor will always be generated automatically by the compiler, if we do not interdict this by deleting it.\n\nThe C++ Core Guidelines have something to say on this topic:\n\n[C.20: If you can avoid defining default operations, do](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-zero)\n> In this case the compiler will auto-generate them for you. This is commonly known as **\"the rule of zero\"**.\n\n[C.21: If you define or =delete any copy, move, or destructor function, define or =delete them all](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-five)\n> The semantics of copy, move, and destruction are closely related, so if one needs to be declared, the odds are that others need consideration too. This is commonly known as **\"the rule of five\"**.\n\n**The Rule of Six**<br>\nextends C.21 to include also the default constructor. The latter is special and, thus, not always include.\n\n**But why?**<br>\nBecause the six special member functions are closely related and the C++ standard is a little bit unintuitive w.r.t. auto-generation when you start selectively defining only some of them. The following table is attributed to *Howard Hinnant (2014)*:<br><br>\n<img src=\"https://www.heise.de/imgs/18/3/6/8/1/8/4/5/DefaultDelete-6dc0661da2aa1431.png\">\n<br><br>\nNote that **user-defined** does not only mean that you implement the constructor/assignment operator, but also includes deleting it (`=delete`) and even explicitely requesting the default variant (`=default`). For full details see \n[Programmiersprache C++: Rule of Zero, or Six](https://www.heise.de/blog/Programmiersprache-C-Rule-of-Zero-or-Six-7463520.html), from which we also borrow the following *cautionary* example:","metadata":{},"id":"4ef8a01b-806b-43b5-9401-5b266143b9e8"},{"cell_type":"code","source":"%%file problematic.cpp\n\n#include <cstddef>\n\nclass BigArray {\n\npublic:\n    BigArray( std::size_t len ): len_( len ), data_( new int[ len ] ) {}\n\n    ~BigArray() {\n        delete[] data_;\n    }\n\nprivate:\n  size_t len_;\n  int* data_;\n    \n};\n\nint main() {\n  \n    BigArray bigArray1( 1000 );\n    \n    BigArray bigArray2( 1000 );\n    \n    bigArray2 = bigArray1;\n\n}","metadata":{"trusted":true},"execution_count":2,"outputs":[{"name":"stdout","output_type":"stream","text":"Overwriting problematic.cpp\n"}],"id":"ebe84f93-58ad-47c0-8ae6-9817fab5b867"},{"cell_type":"code","source":"!g++ problematic.cpp","metadata":{"trusted":true},"execution_count":4,"outputs":[],"id":"e45f50e1-2fe8-4a3b-ba00-05edcc21a62b"},{"cell_type":"markdown","source":"Okay, compilation worked. However, when we execute the program we get this:","metadata":{},"id":"79d80da7-ecbe-45d9-9fda-20cfd4af2831"},{"cell_type":"code","source":"!./a.out","metadata":{"trusted":true},"execution_count":6,"outputs":[{"name":"stdout","output_type":"stream","text":"double free or corruption (!prev)\nAborted (core dumped)\n"}],"id":"2510358b-c716-472d-a0f1-ad7bae1546bb"},{"cell_type":"markdown","source":"What is the reason behind this issue?\n\n- Being good C++ programmers, we have implemented a desctructor for `BigArray()` to deallocate the dynamic array `BigArray::data_` when an object of that class is destroyed.\n- Examining the table above, we see that this implies we get the default versions of the other five special member functions. Should be okay, shouldn't it?\n- Well, no:\n  - Examine line 26 closer. There we have an assignment. The right-hand side is the named object `bigArray1`, so it is an l-value.\n  - Thus, the copy assignment operator will be used here.\n  - Its default version will simply generate a copy of all data members. Hence we get a *flat copy* of `bigArray1::data_` and `bigArray1::data_` and `bigArray2::data_` will point to the same memory address.\n  - The destructor, however, assumes *ownership* of `data_`. So when both objects go out-of-scope at the end of the program (in line 28), the d'tor of `bigArray1` and that of `bigArray2` both attempt to free the same memory address.\n- So in the end the reason for the problem is that we neglected the rule of zero/five/six.","metadata":{},"id":"78455c0f-b267-436c-a1da-72951b83651d"},{"cell_type":"markdown","source":"When we implement the special member functions we should, of course, avoid confusion be adhering to the following\n\n[C.22: Make default operations consistent](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-matched)\n\nSo, if we implement the copy assignment for `BigArray` to make it a deep copy, the same should hold for the copy constructor [and naturally the move assignment/constructor] like we did in the part on *move semantics*.","metadata":{},"id":"5d9d26b5-05ef-4932-95fb-d5a14b78b501"},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[],"id":"865bd92a-b7ce-47b9-9058-edcb272db45e"},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[],"id":"719ec3e2-f84b-4109-ab81-ad3b1b8ce71f"}]}
\ No newline at end of file
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "id": "4c244310-2195-4b50-a923-df0b9ba15f7d",
+   "metadata": {},
+   "source": [
+    "# Move Semantics"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "92b6cf27-b829-4efd-9395-e38f3a330bb4",
+   "metadata": {},
+   "source": [
+    "In order to understand what this is about let us take a look at an example from Rainer Grimm's blog [\"Modernes C++ in der Praxis\"](https://www.linux-magazin.de/ausgaben/2012/12/c-11/):"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "id": "4ffa9b8d-02be-4919-882c-18556a6ded7d",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include <algorithm>\n",
+    "#include <iostream>\n",
+    "#include <vector>\n",
+    "\n",
+    "class BigArrayCopy {\n",
+    "\n",
+    "public:\n",
+    "\n",
+    "  BigArrayCopy( size_t len ) : len_( len ), data_( new int[ len ] ) {}\n",
+    "\n",
+    "  BigArrayCopy( const BigArrayCopy& other ) : len_( other.len_ ),\n",
+    "                                              data_( new int[ other.len_ ] ) {\n",
+    "    std::cout << \"copy construction of \" << other.len_ << \" elements\" << std::endl;\n",
+    "    std::copy( other.data_, other.data_ + len_, data_ );\n",
+    "  }\n",
+    "\n",
+    "  BigArrayCopy& operator=( const BigArrayCopy& other ) {\n",
+    "\n",
+    "    std::cout << \"copy assignment of \" << other.len_ << \" elements\" << std::endl;\n",
+    "\n",
+    "    if( this != &other ) {\n",
+    "\n",
+    "      delete[] data_;\n",
+    "      len_ = other.len_;\n",
+    "      data_ = new int[ len_ ];\n",
+    "\n",
+    "      std::copy( other.data_, other.data_ + len_, data_ );\n",
+    "\n",
+    "    }\n",
+    "\n",
+    "    return *this;\n",
+    "  }\n",
+    "\n",
+    "  ~BigArrayCopy() {\n",
+    "    if( data_ != nullptr ) {\n",
+    "      delete[] data_;\n",
+    "    }\n",
+    "  }\n",
+    "    \n",
+    "private:\n",
+    "\n",
+    "  size_t len_;\n",
+    "  int* data_;\n",
+    "\n",
+    "};"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "id": "dd1e7485-7b8e-4728-a217-df899e07d6d8",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "copy construction of 11111111 elements\n",
+      "copy construction of 11111111 elements\n",
+      "copy assignment of 22222222 elements\n",
+      "copy construction of 33333333 elements\n",
+      "copy construction of 11111111 elements\n"
+     ]
+    }
+   ],
+   "source": [
+    "int main() {\n",
+    "    \n",
+    "  std::vector< BigArrayCopy > myVec;\n",
+    "\n",
+    "  BigArrayCopy bArray( 11111111 );\n",
+    "  BigArrayCopy bArray2( bArray );\n",
+    "\n",
+    "  myVec.push_back( bArray );\n",
+    "  bArray = BigArrayCopy( 22222222 );\n",
+    "  myVec.push_back( BigArrayCopy( 33333333 ) );\n",
+    "\n",
+    "}\n",
+    "\n",
+    "main();"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "333e1d6f-aab5-4c70-859d-db4dafb02981",
+   "metadata": {},
+   "source": [
+    "Let us analyse the output. Where do the five copies come from?\n",
+    "<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>\n",
+    "_____"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "6893f7df-99e0-44e9-a79f-8c6e8acba110",
+   "metadata": {},
+   "source": [
+    "We can change that by adding a **move constructor** and a **move assignment operator** to our class:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "id": "e3784252-4e44-4260-a5a1-d2b92f92e264",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include <algorithm>\n",
+    "#include <iostream>\n",
+    "#include <vector>\n",
+    "\n",
+    "using std::cout;\n",
+    "using std::endl;\n",
+    "using std::vector;\n",
+    "\n",
+    "class BigArray {\n",
+    "\n",
+    "public:\n",
+    "\n",
+    "  BigArray( size_t len ) : len_( len ), data_( new int[ len ] ) {}\n",
+    "\n",
+    "  BigArray( const BigArray& other ) : len_( other.len_ ),\n",
+    "                                              data_( new int[ other.len_ ] ) {\n",
+    "    cout << \"copy construction of \" << other.len_ << \" elements\" << endl;\n",
+    "    std::copy( other.data_, other.data_ + len_, data_ );\n",
+    "  }\n",
+    "\n",
+    "  BigArray& operator=( const BigArray& other ) {\n",
+    "\n",
+    "    cout << \"copy assignment of \" << other.len_ << \" elements\" << endl;\n",
+    "\n",
+    "    if( this != &other ) {\n",
+    "\n",
+    "      delete[] data_;\n",
+    "      len_ = other.len_;\n",
+    "      data_ = new int[ len_ ];\n",
+    "\n",
+    "      std::copy( other.data_, other.data_ + len_, data_ );\n",
+    "\n",
+    "    }\n",
+    "\n",
+    "    return *this;\n",
+    "  }\n",
+    "\n",
+    "  BigArray( BigArray&& other ) : len_( other.len_ ),\n",
+    "                                              data_( other.data_ ) {\n",
+    "    cout << \"move construction of \" << other.len_ << \" elements\" << endl;\n",
+    "    other.len_ = 0;\n",
+    "    other.data_ = nullptr;\n",
+    "  }\n",
+    "\n",
+    "  BigArray& operator=( BigArray&& other ) {\n",
+    "\n",
+    "    cout << \"move assignment of \" << other.len_ << \" elements\" << endl;\n",
+    "\n",
+    "    if( this != &other ) {\n",
+    "\n",
+    "      delete[] data_;\n",
+    "\n",
+    "      len_ = other.len_;\n",
+    "      data_ = other.data_;\n",
+    "\n",
+    "      other.len_ = 0;\n",
+    "      other.data_ = nullptr;\n",
+    "    }\n",
+    "\n",
+    "    return *this;\n",
+    "  }\n",
+    "\n",
+    "  ~BigArray() {\n",
+    "    if( data_ != nullptr ) {\n",
+    "      delete[] data_;\n",
+    "    }\n",
+    "  }\n",
+    "private:\n",
+    "\n",
+    "  size_t len_;\n",
+    "  int* data_;\n",
+    "\n",
+    "};"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "id": "ebbe2374-3d00-4340-98a0-9352ff8ca2cb",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "copy construction of 11111111 elements\n",
+      "copy construction of 11111111 elements\n",
+      "move assignment of 22222222 elements\n",
+      "move construction of 33333333 elements\n"
+     ]
+    }
+   ],
+   "source": [
+    "int main() {\n",
+    "    \n",
+    "  std::vector< BigArray > myVec;\n",
+    "  myVec.reserve(2); // get's rid of the final copy operation, when myVec was reallocated\n",
+    "\n",
+    "  BigArray bArray( 11111111 );\n",
+    "  BigArray bArray2( bArray );\n",
+    "\n",
+    "  myVec.push_back( bArray );\n",
+    "  bArray = BigArray( 22222222 );\n",
+    "  myVec.push_back( BigArray( 33333333 ) );\n",
+    "\n",
+    "}\n",
+    "\n",
+    "main();"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "2fead1ad-cee3-4a0d-ba70-b14066a349b9",
+   "metadata": {},
+   "source": [
+    "As we can see the assignment and copying of the two temporaries in lines # 10 and 11 now uses our **move semantics**.\n",
+    "\n",
+    "But what is does the **\"&&\"** in the interface of the two methods represent? It is an **r-value reference**.\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "a2f2682a-1823-46c4-a444-a23fc192ef17",
+   "metadata": {},
+   "source": [
+    "### R-Value References\n",
+    "\n",
+    "First of all, what are \"r-values\"? Complicated question, short (simplified) answer: stuff that can appear on the right-hand side of an assignment, hence the name (originally). Examples of r-values are:\n",
+    "\n",
+    "- temporary objects\n",
+    "- unnamed objects\n",
+    "- objects whose address is undeterminable\n",
+    "\n",
+    "such as\n",
+    "\n",
+    "```\n",
+    "int fourtyTwo = 42;\n",
+    "std::string a = std::string( \"rhs is an rvalue\");\n",
+    "std::string b = std::string( \"r\" ) +  std::string( \"-value\" );\n",
+    "std::string c = a + b;\n",
+    "std::string d = std::move(b);\n",
+    "```\n",
+    "\n",
+    "The last line is especially interesting. [std::move](https://en.cppreference.com/w/cpp/utility/move) effectively returns an r-value reference for its argument:\n",
+    "\n",
+    "\n",
+    "> std::move is used to indicate that an object t may be \"moved from\", i.e. allowing the efficient transfer of resources from t to another object. In particular, std::move produces an xvalue expression that identifies its argument t. It is exactly equivalent to a static_cast to an rvalue reference type. "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "id": "7c569151-43ef-44a8-babc-6928ca17b626",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "int value = 1;\n",
+    "int& lRef1 = value;\n",
+    "// int&& rRef1 = value;\n",
+    "int&& rRef2 = 1;\n",
+    "const int& lRef2 = 1;"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "1ad6ad50-160f-49d9-b486-2ac84c1cbdbb",
+   "metadata": {},
+   "source": [
+    "An r-value can bind to an r-value reference, but also to a constant l-value reference. That's why `BigArrayCopy` worked after all. However, binding to an r-value reference, if possible has **higher precedence**. That's what we need in `BigArray` for the move methods."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "a0fd8007-04cc-43a5-8c29-07de525a132b",
+   "metadata": {},
+   "source": [
+    "#### STL Containers"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "87780808-08d4-4463-8783-5b21114dc9f6",
+   "metadata": {},
+   "source": [
+    "The containers of the STL support move semantics:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "id": "8e2a7e79-bcd5-4207-a36e-72bcae38b187",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Writing std::move.cpp\n"
+     ]
+    }
+   ],
+   "source": [
+    "%%file std::move.cpp\n",
+    "\n",
+    "#include <iostream>\n",
+    "#include <vector>\n",
+    "#include <string>\n",
+    "\n",
+    "void show( const std::vector< std::string >& vector, const std::string& name ) {\n",
+    "  std::cout << \"Length of vector '\" << name << \"': \" << vector.size()\n",
+    "            << \", contents:\" << std::endl;\n",
+    "  for( const auto& str: vector ) {\n",
+    "    std::cout << str << std::endl;\n",
+    "  }\n",
+    "  std::cout << std::endl;\n",
+    "}\n",
+    "\n",
+    "int main() {\n",
+    "\n",
+    "  std::vector< std::string > src{ \"Move\", \"Semantics\", \"in\", \"the\", \"STL\" };\n",
+    "  show( src, \"src\" );\n",
+    "\n",
+    "  std::vector< std::string > dst{ std::move(src) };\n",
+    "  show( dst, \"dst\" );\n",
+    "  show( src, \"src\" );\n",
+    "\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "id": "fb060696-86ce-440e-8193-5cf540ee6a32",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "!g++ std::move.cpp"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "id": "4712b2fd-4f11-4328-889c-bce3ed9c727e",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Length of vector 'src': 5, contents:\n",
+      "Move\n",
+      "Semantics\n",
+      "in\n",
+      "the\n",
+      "STL\n",
+      "\n",
+      "Length of vector 'dst': 5, contents:\n",
+      "Move\n",
+      "Semantics\n",
+      "in\n",
+      "the\n",
+      "STL\n",
+      "\n",
+      "Length of vector 'src': 0, contents:\n",
+      "\n"
+     ]
+    }
+   ],
+   "source": [
+    "!./a.out"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "b8b8c25b-ac7c-4263-9dcc-5cc1ea0f5408",
+   "metadata": {},
+   "source": [
+    "Additionally the also support **`move iterators`**. See this nice example on [Fluent C++](https://www.fluentcpp.com/2017/04/25/move-iterators)."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "aa620485-bd12-4727-9c7f-d07f66d0e62a",
+   "metadata": {},
+   "source": [
+    "# Rule of Six / Five / Zero"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "6549163b-b1f8-4d7b-9126-0674f01d0022",
+   "metadata": {},
+   "source": [
+    "The advantage of *move semantics* is that it helps to avoid unnecessary copy operations in order to increase performance. However, it also means that a simple class now could implement the following *six different construction/destruction/assigment methods*:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "id": "381417d5-fe96-4249-a509-29fcfdd5615b",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "class simple {\n",
+    "    \n",
+    "    // (1) Default Constructor\n",
+    "    simple() {};\n",
+    "    \n",
+    "    // (2) Destructor\n",
+    "    ~simple(){};\n",
+    "    \n",
+    "    // (3) Copy Constructor\n",
+    "    simple( const simple& other ) {};\n",
+    "    \n",
+    "    // (4) Copy Assigment\n",
+    "    simple& operator= ( const simple& other ) {\n",
+    "        simple* aux = new( simple );\n",
+    "        // copy stuff from other to aux\n",
+    "        return *aux;\n",
+    "    };\n",
+    "    \n",
+    "    // (5) Move Constructor\n",
+    "    simple( const simple&& other ) {};\n",
+    "    \n",
+    "    // (6) Move Assignment\n",
+    "    simple& operator= ( const simple&& other ) {\n",
+    "        simple* aux = new( simple );\n",
+    "        // move stuff from other to aux\n",
+    "        // and potentially set other to 'zero'\n",
+    "        return *aux;\n",
+    "    };\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "4ef8a01b-806b-43b5-9401-5b266143b9e8",
+   "metadata": {},
+   "source": [
+    "The question is, what happens, if we implement none or only some of these methods? For example we know that a default constructor will always be generated automatically by the compiler, if we do not interdict this by deleting it.\n",
+    "\n",
+    "The C++ Core Guidelines have something to say on this topic:\n",
+    "\n",
+    "[C.20: If you can avoid defining default operations, do](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-zero)\n",
+    "> In this case the compiler will auto-generate them for you. This is commonly known as **\"the rule of zero\"**.\n",
+    "\n",
+    "[C.21: If you define or =delete any copy, move, or destructor function, define or =delete them all](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-five)\n",
+    "> The semantics of copy, move, and destruction are closely related, so if one needs to be declared, the odds are that others need consideration too. This is commonly known as **\"the rule of five\"**.\n",
+    "\n",
+    "**The Rule of Six**<br>\n",
+    "extends C.21 to include also the default constructor. The latter is special and, thus, not always include.\n",
+    "\n",
+    "**But why?**<br>\n",
+    "Because the six special member functions are closely related and the C++ standard is a little bit unintuitive w.r.t. auto-generation when you start selectively defining only some of them. The following table is attributed to *Howard Hinnant (2014)*:<br><br>\n",
+    "<img src=\"https://www.heise.de/imgs/18/3/6/8/1/8/4/5/DefaultDelete-6dc0661da2aa1431.png\">\n",
+    "<br><br>\n",
+    "Note that **user-defined** does not only mean that you implement the constructor/assignment operator, but also includes deleting it (`=delete`) and even explicitely requesting the default variant (`=default`). For full details see \n",
+    "[Programmiersprache C++: Rule of Zero, or Six](https://www.heise.de/blog/Programmiersprache-C-Rule-of-Zero-or-Six-7463520.html), from which we also borrow the following *cautionary* example:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 18,
+   "id": "ebe84f93-58ad-47c0-8ae6-9817fab5b867",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Overwriting problematic.cpp\n"
+     ]
+    }
+   ],
+   "source": [
+    "%%file problematic.cpp\n",
+    "\n",
+    "#include <cstddef>\n",
+    "#include <utility>\n",
+    "\n",
+    "class BigArray {\n",
+    "\n",
+    "public:\n",
+    "    BigArray( std::size_t len ): len_( len ), data_( new int[ len ] ) {}\n",
+    "\n",
+    "    ~BigArray() {\n",
+    "        delete[] data_;\n",
+    "    }\n",
+    "\n",
+    "private:\n",
+    "  size_t len_;\n",
+    "  int* data_;\n",
+    "    \n",
+    "};\n",
+    "\n",
+    "int main() {\n",
+    "  \n",
+    "    BigArray bigArray1( 1000 );\n",
+    "    \n",
+    "    BigArray bigArray2( 1000 );\n",
+    "    \n",
+    "    bigArray2 = std::move( bigArray1 );\n",
+    "\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 19,
+   "id": "e45f50e1-2fe8-4a3b-ba00-05edcc21a62b",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "!g++ problematic.cpp"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "79d80da7-ecbe-45d9-9fda-20cfd4af2831",
+   "metadata": {},
+   "source": [
+    "Okay, compilation worked. However, when we execute the program we get this:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 20,
+   "id": "2510358b-c716-472d-a0f1-ad7bae1546bb",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "double free or corruption (!prev)\n",
+      "Aborted (core dumped)\n"
+     ]
+    }
+   ],
+   "source": [
+    "!./a.out"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "78455c0f-b267-436c-a1da-72951b83651d",
+   "metadata": {},
+   "source": [
+    "What is the reason behind this issue?\n",
+    "\n",
+    "- Being good C++ programmers, we have implemented a desctructor for `BigArray()` to deallocate the dynamic array `BigArray::data_` when an object of that class is destroyed.\n",
+    "- Examining the table above, we see that this implies we get the default versions of the other five special member functions. Should be okay, shouldn't it?\n",
+    "- Well, no:\n",
+    "  - Examine line 26 closer. There we have an assignment. The right-hand side is the named object `bigArray1`, so it is an l-value.\n",
+    "  - Thus, the copy assignment operator will be used here.\n",
+    "  - Its default version will simply generate a copy of all data members. Hence we get a *flat copy* of `bigArray1::data_` and `bigArray1::data_` and `bigArray2::data_` will point to the same memory address.\n",
+    "  - The destructor, however, assumes *ownership* of `data_`. So when both objects go out-of-scope at the end of the program (in line 28), the d'tor of `bigArray1` and that of `bigArray2` both attempt to free the same memory address.\n",
+    "- So in the end the reason for the problem is that we neglected the rule of zero/five/six."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "5d9d26b5-05ef-4932-95fb-d5a14b78b501",
+   "metadata": {},
+   "source": [
+    "When we implement the special member functions we should, of course, avoid confusion be adhering to the following\n",
+    "\n",
+    "[C.22: Make default operations consistent](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-matched)\n",
+    "\n",
+    "So, if we implement the copy assignment for `BigArray` to make it a deep copy, the same should hold for the copy constructor [and naturally the move assignment/constructor] like we did in the part on *move semantics*."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "865bd92a-b7ce-47b9-9058-edcb272db45e",
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "C++14",
+   "language": "C++14",
+   "name": "xcpp14"
+  },
+  "language_info": {
+   "codemirror_mode": "text/x-c++src",
+   "file_extension": ".cpp",
+   "mimetype": "text/x-c++src",
+   "name": "c++",
+   "version": "14"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}