# Todo - [x] Example with string - [x] Example with matrices - [x] Eager Evaluation - [x] Return value optimization - [x] Cost of virtual functions - [x] Virtualizing non member functions - [x] Requiring or prohibiting heap-based objects - [x] Multiple Dispatching - [x] Finish More Effective C++ - [ ] Difference between const and constexpr with assembly explanation - [ ] Check Combine C++ and C (for personnal purpose not for the guide) - [ ] Check C++17 specs - [ ] Check that all snippet of codes compile - [ ] Organize information into chapters ## C++ Tips ### <ins>Lazy evaluation:</ins> **Principle: Do not evaluate an expression until the results is required.** This method might save computations whose results are not used. Here are 4 example of lazy evaluation application areas: - **Reference counting:** Count references to objects instead of always copying data. - **Distinguish Reads from Write:** When using reference counting, create new copie of data only when using *operator[]* to write. - **Lazy Fetching:** When dealing with large objects, read no data from object until a specific attribute is asked for Read/Write. - **Lazy Evaluation:** When making operations on structure, store the operation in a structure and defer computation until result is required. #### <ins>Example 1:</ins> ```C++ String s1 = "Hello"; String s2 = s1; //Calls string copy ctor which performs an allocation //then a strcopy to copy data //(s1 and s2 have their own copy of the value) ``` This is how data is stored, however it is not needed to have data duplicated until s2 is modified. <div style="text-align:center"><img src="https://i.imgur.com/zxaUBIm.png" /></div> One method is to implement a struct nested in String that holds a *referenceCounter* and avoid data duplication until it is really needed. Here is a graphical representation of this model: <div style="text-align:center"><img src="https://i.imgur.com/Ov7zKM4.png" /></div> Snippet of code representing this model. ```C++ class String { struct StringValue { //Init data and strcopy initValue inside //Set refCount to 1 StringValue(const char* initValue); //Delete data ~StringValue(); size_t m_sRefCount; char* m_pData; }; String(const char* data="") : m_pValue(new StringValue(data)) { } //Destroy StringValue ~String() { delete m_pValue; } //Copy constructor String(const String& rhs) { if (m_pValue = rhs.m_pValue) return *this; //By doing this, we postpone data duplication // data evulation consisting on: creating a new char[] and calling strcopy if (--m_pValue->m_sRefCount == 0) delete m_pValue; m_pValue = rhs.m_pValue; m_pValue->m_sRefCount++; return *this; } //Evaluation of duplication operation void EvaluateDuplication() { //Decrease reference counter m_pValue->m_sRefCount--; //Creates its own StringValue m_pValue = new StringValue(m_pValue->m_pData); } //Writing operation: data duplication must be evaluated void ToLowerCase() { if (m_pValue > 1) EvaluateDuplication(); //... LowerCase operation } StringValue* m_pValue; }; ``` #### <ins>Example 2:</ins> Here is a class representing a 2D vector: ```C++ struct Vec2 { float m_x; float m_y; Vec2(float x, float y) : m_x(x), m_y(y) {} Vec2 operator+(const Vec2& rhs) const { return Vec2(m_x + rhs.m_x, m_y + rhs.m_y); } float dot(const Vec2& rhs) { return m_x * rhs.m_x + m_y * rhs.m_y; } }; ``` Now, let's considerate a function foo that sums two Vec2 and compute the dot product with a third Vec2. ```C++ float foo(const Vec2& A, const Vec2& B) { Vec2 C(2.0f, 3.0f); //Here is called the ctor of vec2 to build a Vec2 and //the result of A + B is copied, however this freshly //created Vec2 is destroied at the end of this call return (A + B).dot(C); } ``` Using lazy evaluation, it is possible to save a call to Vec2 ctor&dtor because there is no need for Vec2::operator+ to be called. ```C++ //Create a structure that will hold sum operand and //compute result only if needed struct addVec2 { addVec2(const Vec2& lhs, const Vec2& rhs) : a(lhs), b(rhs) //Here we save Vec2 ctor/dtor call float dot(const Vec2& v); //Evaluates sum Vec2 Eval(); Vec2 a; Vec2 b; } Vec2 addVec2::Eval() { return Vec2(lhs.m_x + rhs.m_x, lhs.m_y + rhs.m_y); } float addVec2::dot(const Vec2& v) { return a.dot(v) + b.dot(v); } //Let's change Vec2::operator+ //Do not forget to change declaration addVec2 Vec2::operator+(const Vec2& lhs, const Vec2& rhs) { return addVec2(lhs, rhs); } ``` ### <ins>Eager evaluation:</ins> **Principle: Doing things before they are needed to minimize further computations.** When you expect computation to be resquested frequently, it is possible to reduce the cost requests by precomputing the results. #### <ins>Example 1</ins> Let's consider a database containing information about clients and you expect many queries on clients age. It is possible to use local caching to store information about age and avoid querying the database all the time. ```C++ int getClientAge(const std::string& clientName) { //Executed once static std::map<std::string, int> mapAges; auto it = mapAges.find(clientName); //Data not contained in map if (it == mapAges.end()) { int age = //Query database //Add queried data to local cache mapAges[clientName] = age; return age; } //Queried data is already in local cache //save a call to database else return it->second; } ``` #### <ins>Example 2</ins> Suppose we have a *Vector* containing numeric data and we expect min, max to be asked frequently. It is better to precompute them when data is modified than having to compute them each time they are required. ```C++ struct Vector { Vector(const int* values, int nbValues) { data = new int[nbValues];//Init arr std::memcpy(data, values, nbValues);//cpy data size = nbValues;//save size //Precompute min/max min = MAX_INT; max = MIN_INT; int val = 0; for (int i = 0; i < nb_values; val = a[i++], min = std::min(min, val), max = std::max(max, val)) continue; } //Precompute min/max on append //Update also on remove|operator[](write) //(!Careful there might be multiple occurences of an element) void Append(int elt) { min = std::min(min, elt); max = std::max(max, elt); size++; //Append data.... } int GetMin() { return min; } int GetMax() { return max; } ~Vector//delete data int min; int max; int* data; int size; }; ``` ### <ins>Return by value optimization:</ins> First, let's deal with temporary object creation with the following example: ```C++ void printRef(string& p); void printConstRef(const string& p); void foo() { char test[5] = "test"; printRef(test); //Does not compile, this would create a temporary string //and this temporary string would be edited and not test //which is not the behavior expected by programmer printConstRef(test);//Here: Call to string ctor with test value //Temporary string is destroied at the end of the function } ``` Here is another example of temp ```C++ class FooClass { //ctor FooClass(int val = 0); //dtor ~FooClass(); //Copy ctor FooClass(const FooClass& o); //Value attr int m_val; //PostFix operator: Takes int argument and compiler pass 0 as arg //Returns an object holding the result's old value and //increments current const FooClass operator++(int); //Why return a const ? //Otherwise, this would be possible: //FooClass i; //i++++; ////Returns an object holding the result of the sum operation const FooClass operator+(const FooClass& lhs, const FooClass& rhs) { //Temp object FooClass result(lhs.val + rhs.val); return result; }// result is destroied and value is copied in temp obj to return } ``` Some techniques to avoid returning an object could be: ```C++ //Returning a pointer const FooClass* operator+(const FooClass& lhs, const FooClass& rhs); ``` <span style="background-color: #DF0000; color: #FFFFFF"> However, this is not a convenient way since it introduces a pointer that should be deleted after each operation.</span> ```C++ FooClass a; FooClass b; FooClass c = *(a + b); ``` Another possible technique is: ```C++ //Returning a reference const FooClass& operator+(const FooClass& lhs, const FooClass& rhs); ``` <span style="background-color: #DF0000; color: #FFFFFF"> But, this is not a good way to achieve avoid returning an object since the referenced object is created inside of operator+ and is destroied after the function call. Therefore this leads to an undefined behavior.</span> There is no way to avoid returning the object by value, so instead of trying si hard to eliminate the object-value we should just try to remove the cost associated with its creation. By returning a constructor argument, we can eliminate the cost of temporary object. ```C++ //Returns an object holding the result of the sum operation const FooClass operator+(const FooClass& lhs, const FooClass& rhs) { return FooClass(lhs.val + rhs.val); } ``` It is also possible to eliminate the temporary object created by the operator call, eliminating the call to the operator with *inline*. <span style="background-color: #00A000; color: #FFFFFF"> So, the most efficient way to write a function that returns an object is:</span> ```C++ //This eliminate the call to operator+ inline const FooClass operator+(const FooClass& lhs, const FooClass& rhs) { return FooClass(lhs.val + rhs.val); } ``` ### <ins>Cost of virtual functions</ins> <ins><i><b>virtual tables (vtabls)</b></i></ins>: Array that holds pointer(*vptr*) to the implementation of virtual functions of a class. Each class declaring virtual functions or inheriting from a class that has virtual functions has its own *vtbl*. Non virtual functions are not in the *vtabl* since they are implemented like ordinary C functions. Class *ctor* cannot be **virtual** because when constructor of a class is executed there is no *vtable* in the memory, means no *vptr* defined yet. **(Dive deeper into this)** Here are two examples of *vtabls*: ```C++ class Bar { public: Bar();//Cannot be virtual virtual ~Bar(); virtual void f1(); virtual int f2(); void f3(char c); }; ``` Its *vtabl* looks like: ![](https://i.imgur.com/4CDkO16.png) Now, if we have: ```C++ class Baz: public Bar { public: Baz();//Cannot be virtual virtual ~Baz();//redefined function virtual void f1();//redefined function virtual int f4(float f); }; ``` Then its *vtbl* will look like: ![](https://i.imgur.com/fmKWla3.png) **What about the size of *vtabls* in a class ?** It is proportionnal to the number of virtual functions in that class. **Where should the *vtbl* for a class be contained ?** **<ins>Two strategies are adopted</ins>** **Brute-force**: Each .obj file that might need a vtbl contains a copy, then the linker deletes duplicate copies. **Heuristic**: Usually, it is placed in the object file containing the definition of **the first non-inline non-pure virtual function** in that class. If all virtual functions of a class are **inline** then most **heuristic-based** implementation creates a copy of the class *vtbl* in each object file using this class. That is why, compilers nowadays ignores the **inline** for virtual functions and a good practice is to avoid declaring **inline virtual** functions. The reason why it cannot work is: **inline** is handled during **compilation**, however virtual functions cannot now which function to call until runtime. **<ins>Run-Time Type Information (*RTTI*)</ins>** Gives information about a class, this information if of type `type_info` can be accessed using `typeid`. **Summary of this chapter**: ![](https://i.imgur.com/0mPNJ6A.png) ### <ins>Virtualizing Constructor & non member functions:</ins> **<ins>Constructor</ins>** As mentionned previously, it is not possible to have a virtual constructor. But, what we can have is a static function that creates the correct class instance based on the input. ```C++ class Foo { public: //ctor|dtor|... static Foo* Create(int id) { if (id == 0) return new Foo(); if (id == 1) return new Bar(); if (id == 2) return new Baz(); } }; class Bar : public Foo { public: //ctor|dtor|... }; class Baz : public Foo { public: //ctor|dtor|... }; ``` And, it is also possible to write a function that act as a virtual copy constructor. ```C++ class Foo { public: //ctor|dtor|... virtual Foo* clone() { return new Foo(*this); } }; class Bar : public Foo { public: //ctor|dtor|... virtual Foo* clone() { return new Bar(*this); } }; class Baz : public Foo { public: //ctor|dtor|... virtual Foo* clone() { return new Baz(*this); } }; ``` **<ins>Non-member functions</ins>** If we consider the **write** `operator<<`, it is not possible to have it inside the class so it cannot be virtual. Another approach is to have a virtual function inside of the class that will handle the operation and call it inside `operator<<` (just like *Visitor Design Pattern*). ```C++ class Foo { public: //ctor|dtor|... virtual ostream& print(ostream& os) const { return os << "Foo" << std::endl; } }; class Bar : public Foo { public: //ctor|dtor|... virtual ostream& print(ostream& os) const override { return os << "Bar" << std::endl; } }; class Baz : public Foo { public: //ctor|dtor|... virtual ostream& print(ostream& os) const override { return os << "Baz" << std::endl; } }; ostream& operator<<(ostream& os, const Foo& obj) { return obj.print(os); } ``` ### <ins>Requiring or prohibiting heap-based objects</ins> *These are good to know but not primoridal tips.* To require a heap-based object, make ctor/dtor or both private. Usually, it is dtor that is declared private. ```C++ class Foo { //Trying to create an heap based object will generate compile errors //Also it is not possible to public: Foo(); //delete operator cannot be called void destroy() const { delete *this; }; private: ~Foo(); }; ``` To prohibit heap-based object, declare `operator new` private (it is a good practice to also declare `operator delete` private in such a case). ```C++ class Foo { private: static void *operator new(size_t size); static void operator delete(void *ptr); }; ``` However, this will also prohibit to call `operator new` and `operator delete` in derived classes from Bar. Unless, they are redeclared public in the derived class: ```C++ class Bar : public Foo { public: static void *operator new(size_t size) { return ::operator new(size); //malloc(size) also works } static void operator delete(void *ptr) { free(ptr); } }; ``` ### <ins>Multiple Dispatch</ins> **<ins>What is Single/Multiple Dispatching ?</ins>** <span style="background-color: #00A000; color: #FFFFFF">This is when a method is polymorphic on one or multiple parameters.</span> #### Reminder ##### Polymorphism in C++ ![](https://i.imgur.com/ORpR7sS.png) Difference between `compile time` and `run time` techniques is that methods using `compile time` polymorphism knows which method to call at compilation. With virtual functions, C++ allow us to make dynamic single dispatching. ```C++ class A { public: virtual void act() { std::cout << "A is acting" << std::endl; } }; class B: public A { public: virtual void act() { std::cout << "B is acting" << std::endl; } }; int main(void) { A* instance = new B; instance->act(); delete instance; return 0; } ``` **Output:** `B is acting` #### Multiple dynamic dispatching **<ins>What if we want to do multiple dynamic dispatching ?</ins>** Suppose, you are working on a game and you want to create an interaction between objects and you want this interaction to depend on their dynamic types. For this example, we will consider the operation `collide`. If we have this hierarchy: ![](https://i.imgur.com/buDzbIK.png) All classes inherit from `GameObject`, suppose we want: * `Ball` to `bounce` when it collides with another `Ball`. * `Ball` to `damage` when it collides with `Glass`. * `Ball` to `stop` when it collides with `Sponge` * `Glass` to `break` when it collides with `Glass` * `Glass` to `stop` when it collides with `Sponge` * `Sponge` to `merge` when it collides with `Sponge` ##### Using C++ Single Dispatch What we are trying to do is **Multiple Dynamic Disptaching**. The idea that comes first is to handle this with multiple C++ *Single Dynamic Dispatch* using virtual methods: ```C++ class Ball; class Glass; class Sponge; class GameObject { virtual void collide(Ball& o) = 0; virtual void collide(Glass& o) = 0; virtual void collide(Sponge& o) = 0; }; class Ball : public GameObject { virtual void collide(Ball& o) override;//bounce virtual void collide(Glass& o) override;//damage virtual void collide(Sponge& o) override;//stop }; class Glass : public GameObject { virtual void collide(Ball& o) override;//damage virtual void collide(Glass& o) override;//break virtual void collide(Sponge& o) override;//stop }; class Sponge : public GameObject { virtual void collide(Ball& o) override;//stop virtual void collide(Glass& o) override;//stop virtual void collide(Sponge& o) override;//merge }; ``` <ins>This method works but has a major flaw:</ins> This become very painful when dealong with many classes (imagine having 40 classes directly inheriting from `GameObject` and you want to add a 41th then you'd have to write a new `collide` method in these 40 classes and write 40 `collide` method in the new class). ##### Using RTTI and virtual function Another common approach is to use **RTTI** with `if-else` blocks: ```C++ class GameObject { virtual void collide(GameObject& o) = 0; }; //Declare Sponge, Glass //Let's illustrate with Ball class class Ball : public GameObject { virtual void collide(GameObject& o) { const type_info& objType = typeid(o); if (typeid(Ball) == objType) { Ball& b = static_cast<Ball&>(o); //Process collision with Ball } else if (typeid(Glass) == objType) { Glass& b = static_cast<Glass&>(o); //Process collision with Glass } else if (typeid(Sponge) == objType) { Sponge& b = static_cast<Sponge&>(o); //Process collision with Sponge } else //throw Exception } }; ``` This method works, but we have to update every collide function whenever we add a new class or it will throw an exception. This might lead to a bug which could be tough to find. ##### Initializing Emulated Virtual Function Tables (+) To have a project that is less painful to maintain, we do not want to edit previous file every time a new `GameObject` is added to the codebase. It could be great to have a class that holds a map matching pairs of `typeid` with the proper `collide` function that is dynamically editable. ```C++ using namespace std; class CollisionMap//Singleton { public: typedef void (*CollisionFnc)(GameObject&, GameObject&); void addEntry(const string& type1, const string& type2, HitFunctionPtr collisionFunction, bool symmetric = true) { auto collisionTypes = make_pair(type1, type2); auto collisionTypesrev = make_pair(type2, type1); if (m_mapCollisionFnc.find(collisionTypes) != m_mapCollisionFnc.end() || (symmetric && m_mapCollisionFnc.find(collisionTypesrev) != m_mapCollisionFnc.end())) return; m_mapCollisionFnc[collisionTypes] = collisionFunction; if (symmetric) m_mapCollisionFnc[collisionTypesrev] = collisionFunction; } void removeEntry(const string& type1, const string& type2) { auto collisionTypes = make_pair(type1, type2); if (m_mapCollisionFnc.find(collisionTypes) != m_mapCollisionFnc.end()) m_mapCollisionFnc.erase(collisionTypes); } CollisionFnc lookup(const string& type1, const string& type2) { auto collisionTypes = make_pair(type1, type2); if (m_mapCollisionFnc.find(collisionTypes) != m_mapCollisionFnc.end()) return m_mapCollisionFnc[collisionTypes]; return nullptr; } //Singleton instance static CollisionMap& instance(); private: CollisionMap(); CollisionMap(const CollisionMap&); map<pair<string, string>, CollisionFnc> m_mapCollisionFnc; }; ``` Now, we would like to have all `<Key, Value>` pairs added to the map before any collision happens. To handle this have a class that register methods and types in its `ctor`. And create global objects from this class. ```C++ class RegisterCollisionFnc { RegisterCollisionFnc(const string& type1, const string& type2, CollisionMap::HitFunctionPtr fnc, bool symmetric = true) { CollisionMap::instance().addEntry(type1, type2, fnc, symmetric); } }; void collideBallGlass(GameObject&, GameObject&) {/*Do stuff*/} void collideBallSponge(GameObject&, GameObject&) {/*Do stuff*/} //Objects are created before `main` call RegisterCollisionFnc rc1("Ball", "Glass", &collideBallGlass); RegisterCollisionFnc rc2("Ball", "Sponge", &collideBallSponge); int main(void) { //Execution starts return 0; } ``` ##### PS Other techniques are detailed in `More effective C++: 35 new ways to improve your programs and designs` by *Scott Meyers* but the purpose is not to detail all of them here. ### keyword `explicit` > Specifies that a constructor or conversion function(since C\+\+11) or deduction guide(since C\+\+17) is explicit, that is, it cannot be used for implicit conversions and copy-initialization. ```C++ struct A { A(int) { } // converting constructor A(int, int) { } // converting constructor (C++11) operator bool() const { return true; } }; struct B { explicit B(int) { } explicit B(int, int) { } explicit operator bool() const { return true; } }; int main() { A a1 = 1; // OK: copy-initialization selects A::A(int) A a2(2); // OK: direct-initialization selects A::A(int) A a3 {4, 5}; // OK: direct-list-initialization selects A::A(int, int) A a4 = {4, 5}; // OK: copy-list-initialization selects A::A(int, int) A a5 = (A)1; // OK: explicit cast performs static_cast if (a1) { } // OK: A::operator bool() bool na1 = a1; // OK: copy-initialization selects A::operator bool() bool na2 = static_cast<bool>(a1); // OK: static_cast performs direct-initialization // B b1 = 1; // error: copy-initialization does not consider B::B(int) B b2(2); // OK: direct-initialization selects B::B(int) B b3 {4, 5}; // OK: direct-list-initialization selects B::B(int, int) // B b4 = {4, 5}; // error: copy-list-initialization does not consider B::B(int, int) B b5 = (B)1; // OK: explicit cast performs static_cast if (b2) { } // OK: B::operator bool() // bool nb1 = b2; // error: copy-initialization does not consider B::operator bool() bool nb2 = static_cast<bool>(b2); // OK: static_cast performs direct-initialization [](...){}(a4, a5, na1, na2, b5, nb2); // may suppress "unused variable" warnings } ``` <a href="https://en.cppreference.com/w/cpp/language/explicit">Source: cppreference</a> ### Uniform initialization #### Most vexing parse When the compiler cannot distinguish **object creation** and **funciton declaration**. By default: interpret code as function declaration. **Example:** ```C++ void f(double my_dbl) { // int i(int(my_dbl)); <- ambiguity there int i{int(my_dbl)}; // No ambiguity } struct Timer {}; struct TimeKeeper { explicit TimeKeeper(Timer t); int get_time(); }; int main() { //TimeKeeper time_keeper(Timer()); <- ambiguity there TimeKeeper time_keeper{Timer()}; // No ambiguity return time_keeper.get_time(); } ``` ### Pointer aliasing **Pointer aliasing** is when same memory location can be accessed using different pointer names. <div style="text-align:center"> <img src="https://hackmd.io/_uploads/HkTpKXo2a.png"> </div> The <a href="https://docs.xilinx.com/r/en-US/ug1079-ai-engine-kernel-coding/Strict-Aliasing-Rule">**strict aliasing rule**</a> in C/C++ means that pointers are assumed not to alias if they point to fundamentally different types. Aliasing introduces strong constraints on program execution order which avoid parallelization. ![image](https://hackmd.io/_uploads/HkmMiQo3a.png) **To avoid aliasing:** use keyword `__restrict` > When `__restrict` is used, the compiler won't propagate the no-alias property of a variable. <a href="https://learn.microsoft.com/en-us/cpp/cpp/extension-restrict?view=msvc-170">Microsoft</a> Here is the `x86-64` assembly code generated by gcc with and without `__restrict`: <div style="text-align:center"> <img src="https://hackmd.io/_uploads/HkY76Qi2T.png" width=350/> <img src="https://hackmd.io/_uploads/HkKV67o3T.png" width=350/> </div> --- <div style="text-align:center"> <img src="https://hackmd.io/_uploads/SyZC0Qi2p.png" width=350/> <img src="https://hackmd.io/_uploads/HJtZ1Ns3a.png" width=350/> </div> <a href="https://docs.xilinx.com/r/en-US/ug1079-ai-engine-kernel-coding/IsomorphicGraphGroup-Constraint">Source</a> ### Virtual inheritance Solves **diamond inheritance** problem <div style="text-align:center"> <img src="https://hackmd.io/_uploads/H1LQKNEJC.png" height=250/> </div> <br /> <br /> When using **non-virtual inheritance**: ```C++ struct A { int n; A(int x) : n(x) {} }; struct B : A { B() : A(1) {} }; struct C : A { C() : A(2) {} }; struct D : B, C { D() : /*A ctor cannot be called here*/ B(), C() {} }; ``` There is an ambiguity on how to construct A object from D object, since the memory representation of this in memory is: <div style="text-align:center"> <img src="https://hackmd.io/_uploads/r1qeK4NyR.png" height=250/> </div> <br /> <br /> As we can see using `g++ -fdump-lang-class`: ``` ... Class A size=4 align=4 base size=4 base align=4 A (0x0x7fd606f64120) 0 Class B size=4 align=4 base size=4 base align=4 B (0x0x7fd6072919c0) 0 A (0x0x7fd606f64300) 0 // Same for C Class D size=8 align=4 base size=8 base align=4 D (0x0x7fd606f795b0) 0 B (0x0x7fd607291e38) 0 A (0x0x7fd606f646c0) 0 C (0x0x7fd606f84000) 4 A (0x0x7fd606f64720) 4 ... ``` However, when using **virtual inheritance** construction we have: ``` ... Class A size=4 align=4 base size=4 base align=4 A (0x0x7f38d02d6120) 0 Vtable for B B::vtable for B: 3 entries 0 8 8 (int (*)(...))0 16 (int (*)(...))(& typeinfo for B) VTT for B B::VTT for B: 1 entries 0 ((& B::vtable for B) + 24) Class B size=16 align=8 base size=8 base align=8 B (0x0x7f38d06039c0) 0 nearly-empty vptridx=0 vptr=((& B::vtable for B) + 24) A (0x0x7f38d02d6300) 8 virtual vbaseoffset=-24 // Same for C Vtable for D D::vtable for D: 6 entries 0 16 8 (int (*)(...))0 16 (int (*)(...))(& typeinfo for D) 24 8 32 (int (*)(...))-8 40 (int (*)(...))(& typeinfo for D) Construction vtable for B (0x0x7f38d0603e38 instance) in D D::construction vtable for B-in-D: 3 entries 0 16 8 (int (*)(...))0 16 (int (*)(...))(& typeinfo for B) Construction vtable for C (0x0x7f38d02fa000 instance) in D D::construction vtable for C-in-D: 3 entries 0 8 8 (int (*)(...))0 16 (int (*)(...))(& typeinfo for C) VTT for D D::VTT for D: 4 entries 0 ((& D::vtable for D) + 24) 8 ((& D::construction vtable for B-in-D) + 24) 16 ((& D::construction vtable for C-in-D) + 24) 24 ((& D::vtable for D) + 48) Class D size=24 align=8 base size=16 base align=8 D (0x0x7f38d02eb5b0) 0 vptridx=0 vptr=((& D::vtable for D) + 24) B (0x0x7f38d0603e38) 0 nearly-empty primary-for D (0x0x7f38d02eb5b0) subvttidx=8 A (0x0x7f38d02d6780) 16 virtual vbaseoffset=-24 C (0x0x7f38d02fa000) 8 nearly-empty subvttidx=16 vptridx=24 vptr=((& D::vtable for D) + 48) A (0x0x7f38d02d6780) alternative-path ... ``` where there is information about how to construct **base class** when we have **diamond inheritance**. > Based on `cat file_dump.cpp.001l.class | c++filt -n` results <br /> > <a href="https://en.cppreference.com/w/cpp/language/derived_class"> Source: cppreference</a> > Find details about generated assembly code <a href="https://quuxplusone.github.io/blog/2019/09/30/what-is-the-vtt/">here</a> ### Concepts (C++20) > A concept is a named set of requirements. (<a href="https://en.cppreference.com/w/cpp/language/constraints">cppreference</a>) It can be used to specify ### Compile-Time Decorators ### `std::promise` & `std::future`