C++ Common Knowledge - Dawid Zalewski - Meeting C++ 2023
Ғылым және технология
C++ Common Knowledge - Dawid Zalewski - Meeting C++ 2023
Let's face it, you can get by just fine in C++ without knowing what std::launder does or how to use parameter packs with std::index_sequence. However, if you consider yourself a well-rounded C++ programmer, you can't escape being familiar with the one-definition rule or understanding the deterministic object destruction guarantees. That's just C++ common knowledge. Yet, such foundational items are often overlooked and their details are glossed over. Unfortunately so. Let us take a step back and talk about the basics, digging deep into the core language. Let's start making the catalogue of the C++ common knowledge. Besides the topics already mentioned, we'll talk about the lookup rules, friends, initialization, member functions and others!
Пікірлер: 6
*Summary* *introduction* - 00:00 Dawid Zalewsky introduces the topic of common knowledge in C++. - 00:26 Dawid mentions he enjoys reading old programming books to learn about C++'s history and nuances. - 01:21 The talk aims to cover important common knowledge items in C++, some of which may be overlooked or unknown, especially to those new to the language or certain aspects of it. *motivation for the talk* - 01:44 The talk will modernize common knowledge items and will not cover everything from the book with the same title. *buggy program example* - 02:02 A short, buggy C++ program is introduced. - 02:20 The program includes a base class with data members and a conversion operator. - 02:34 A named object class is also shown, which includes a comparator operator. - 03:01 The named object class is used in conjunction with a named heap class. - 03:29 The named heap class inherits from the named collection base and includes data members for tracking the smallest and largest values. - 03:49 The named heap class also follows the rule of five, with proper constructors and destructors. - 04:07 The classes are used together, with a function that requires a collection passed by unique pointer hinting at polymorphism. *compilation and runtime issues* - 04:57 The buggy program compiles with modern C++ standards but has runtime issues. - 05:09 Only a few compiler warnings are triggered, which may be missed. - 05:22 The program crashes or runs indefinitely despite compiling successfully. - 05:35 The program violates more than 10 important best practices in C++. *list of issues in the program* - 05:41 Dawid lists the issues in the program: uninitialized data members, undefined behavior, implicit conversions, assignment vs. initialization confusion, one definition rule violations, exception safety neglect, memory leaks, const correctness issues, infinite recursion, and object slicing. *approach to fixing the program* - 06:45 Dawid proposes to fix the program by walking through 12 different items. - 07:28 Uninitialized variables are indeterminate and can lead to undefined behavior. - 08:05 Removing the user-provided constructor that does nothing helps avoid uninitialized data members. - 09:14 The order of declaration in a class determines the order of member initialization. - 11:20 Data members should be initialized in the correct order to avoid indeterminate values. - 11:41 Assignment should not be confused with initialization, and using curly braces can help clarify initialization in constructors. - 14:08 Implicit conversions should be avoided as they can lead to unexpected behaviors. *issue with implicit conversions and constructors* - 14:25 The named heap class has a converting constructor from a string, which can lead to implicit conversions that may be unintended or nonsensical. - 14:58 Implicit conversions can cause issues, such as allowing nonsensical function calls to compile without error. - 15:43 To prevent these problems, Dawid suggests making the constructor `explicit` to disable implicit conversions. *problem with implicit conversions and comparison* - 16:03 Implicit conversions in comparison operators can lead to unexpected behavior when comparing objects of a base class. - 16:24 The named collection base has an implicit conversion operator to a named object, which has a defined equality operator. - 17:04 Marking the conversion operator as `explicit` can prevent unintended comparisons. *one definition rule and its implications* - 17:17 The one definition rule requires each object to be defined only once across all translation units. - 17:51 A constant defined in a header file can lead to multiple definition errors if included in multiple source files. - 19:01 The linker will complain about multiple definitions of the same object if included in different translation units. - 20:20 To resolve this, you can mark the variable as `inline` or make it `static` or `constexpr`. *importance of object construction and RAII* - 21:15 Only fully constructed objects benefit from RAII (Resource Acquisition Is Initialization). - 21:34 There is a potential problem with resource allocation in constructors that may throw exceptions. - 22:13 If an exception is thrown during construction, the destructor won't be called for the partially constructed object, potentially causing a resource leak. - 24:35 A solution involves delegating to a default constructor that initializes resources to null, allowing the destructor to clean up properly. - 25:42 A better approach is to follow the principle of one class, one resource, to simplify resource management and ensure RAII. - 26:49 With modern C++, you can use `std::unique_ptr` or `std::vector` for automatic memory management. *extra `this` argument in member functions* - 27:15 Member functions have an implicit `this` pointer as an extra argument. - 27:27 Understanding the `this` pointer is important for grasping how more advanced features like `deducing this` work. - 28:04 The named object overloads the inequality comparison operator, which takes an additional implicit `this` parameter. - 28:35 The presence of the `this` pointer in member functions is crucial for understanding object behavior and method calls in C++. *function call chain issue with implicit `this`* - 29:14 There is a canonical implementation involving a member function and a free function that piggybacks on the member function. - 29:32 After inlining, comparing two named objects using the equality operator can lead to a call chain that causes infinite recursion due to rewriting rules in C++20. *infinite recursion issue and its resolution* - 30:28 The infinite recursion occurs due to the compiler rewriting the comparison to call itself repeatedly when the member function does not match the required const qualification. - 33:25 To fix this, the member function should be made const to match the const qualification of the arguments. - 34:11 Making the member function const allows the correct chain of calls to proceed without infinite recursion. *discussion on hidden friends and defaulting functions* - 34:57 The compiler can synthesize certain comparison operators, so it is unnecessary to define both. - 35:16 Hidden friends are used to hide functions from lookup unless the arguments explicitly match the parameters of the function. - 37:03 To prevent unwanted implicit conversions in comparisons, the comparison operator can be moved inside the class and declared as a friend. - 38:43 With C++20, it's possible to default the comparison operator and declare it as a friend, which simplifies the code and ensures the compiler synthesizes the other comparison operators. *necessity of virtual destructors in polymorphic classes* - 39:15 Polymorphic classes need virtual destructors to avoid memory leaks. - 41:07 The named heap and named collection base classes lack virtual destructors, which can cause memory leaks when objects are destroyed through a base class pointer. - 42:09 The fix is to add a virtual destructor to the base class, allowing proper destruction of derived class objects. *issue of object slicing* - 42:49 Object slicing occurs when a derived class object is copied through a base class reference, losing part of the derived class's data. - 43:19 An example of object slicing is shown where a named heap object is copied, but only the base class portion is copied, not the derived class data. *continuation of object slicing issue* - 44:19 Function taking named collection base by copy results in object slicing, copying only part of the object. - 44:50 Object slicing can lead to unexpected behavior and crashes when casting or using the sliced object. - 45:09 A potential fix for object slicing is to ensure the correct class is used when making a copy or passing objects to functions. *conclusion of the talk and recap of knowledge items* - 45:43 The talk concludes with a recap of the 12 common knowledge items discussed. - 45:51 All the mistakes discussed were demonstrated within the first few slides of the presentation. - 46:06 The audience is encouraged to try out the examples themselves to understand the issues better. *Q&A session* - 46:25 A question from the online audience about when to define the `==` operator if the spaceship operator is asked for. - 47:00 The answer is that with the spaceship operator, defining the `==` operator is typically unnecessary. - 48:34 A concern about implicit conversions that the compiler can make, specifically converting comparison operators, is raised. - 48:48 The rule introduced in C++20 allows the compiler to rewrite comparisons when one is lacking. - 50:01 A question about best practices for adding `inline` to `constexpr` variables is discussed. - 50:56 The answer clarifies that `inline` allows multiple definitions and the Linker ensures there is only one object created. - 51:38 A comment is made that if the spaceship operator is explicitly defined and not defaulted, the `==` operator must also be explicitly defaulted. - 52:23 Clarification that the `==` operator is not always provided automatically even if the spaceship operator is present. - 52:35 A question about handling bad memory allocations (`bad_alloc`) is asked. - 52:58 The response suggests that running out of memory is usually a terminal situation for a program, with limited options available. - 53:54 A historical anecdote about memory allocation strategies is shared, including reserving a small amount of memory for emergency use. *closing of the session* - 54:15 The speaker thanks the audience for attending the talk and participating in the Q&A. Disclaimer: I used gpt4-1106 to summarize the video transcript. This method may make mistakes in recognizing words.
Mr. Zalewski always surprises me with his deep technical talks. Really enjoyed it.
Great talk!
Defining operator == is still useful when also defining operator because operator == (and operator !=) can return early when 2 containers don't have the same size.
The explanation for hidden friends was a bit confused. The information that got lost (I had to rewind to double-check) is that named_collection_base and named_object are unrelated types, named_collection_base simply has an implicit conversion to named_object. Hidden friends are still visible in many situations (following ADL rules).