C++ Common Knowledge - Dawid Zalewski - Meeting C++ 2023
Вставка
- Опубліковано 23 січ 2024
- 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! - Наука та технологія
*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.
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.
Great talk!
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).