diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md index b604cb1..41319d2 100644 --- a/CppCoreGuidelines.md +++ b/CppCoreGuidelines.md @@ -1728,6 +1728,7 @@ Value return rules: * [F.43: Never (directly or indirectly) return a pointer to a local object](#Rf-dangle) * [F.44: Return a `T&` when "returning no object" isn't an option](#Rf-return-ref) * [F.45: Don't return a `T&&`](#Rf-return-ref-ref) +* [F.46: `int` is the return type for `main()`](#Rf-main) Other function rules: @@ -2828,6 +2829,28 @@ Functions can't capture local variables or be declared at local scope; if you ne * Warn on use of a named non-generic lambda (e.g., `auto x = [](int i){ /*...*/; };`) that captures nothing and appears at global scope. Write an ordinary function instead. + +### F.46: `int` is the return type for `main()` + +##### Reason + +It's a language rule, but violated through "language extensions" so often that it is worth mentioning. +Declaring `main` (the one global `main` of a program) `void` limits portability. + +##### Example + + void main() { /* ... */ }; // bad, not C++ + + int main() + { + std::cout << "This is the way to do it\n"; + } + +##### Enforcement + +* The compiler should do it +* If the compiler doesn't do it, let tools flag it + ### F.51: Prefer overloading over default arguments for virtual functions ??? possibly other situations? @@ -5095,6 +5118,7 @@ Designing rules for classes in a hierarchy summary: * [C.136: Use multiple inheritance to represent the union of implementation attributes](#Rh-mi-implementation) * [C.137: Use `virtual` bases to avoid overly general base classes](#Rh-vbase) * [C.138: Create an overload set for a derived class and its bases with `using`](#Rh-using) +* [C.139: Use `final` sparingly](#Rh-final) Accessing objects in a hierarchy rule summary: @@ -5475,6 +5499,68 @@ This a relatively rare use because implementation can often be organized into a ##### Example ??? + + +### C.139: Use `final` sparingly + +##### Reason + +Capping a hierarchy with 'final` is rarely needed for logical reasons and can be damaging to the extensibility of a hierarchy. +Capping an individual virtual function with `final` is error-prone as that `final` can easily be overlooked when defining/overriding a set of functions. + +##### Example, bad + + class Widget { /* ... */ }; + + class My_widget final : public Widget { /* ... */ }; // nobody will ever want to improve My_widget (or so you thought) + + class My_improved_widget : public My_widget { /* ... */ }; // error: can't do that + +##### Example, bad + + struct Interface { + virtual int f() = 0; + virtual int g() = 0; + }; + + class My_implementation : public Interface { + int f() override; + int g() final; // I want g() to be FAST! + // ... + }; + + class Better_implementation : public My_implementation { + int f(); + int g(); + // ... + }; + + void use(Interface* p) + { + int x = p->f(); // Better_implementation::f() + int y = p->g(); // My_implementation::g() Surprise? + } + + // ... + + use(new Better_interface{}); + +The problem is easy to see in a small example, but in a large hierarchy with many virtual functions, reliable spotting such problems require tools. +Consistent use of `override` would catch this. + +##### Note + +Claims of performance improvements from `final` should be substantiated. +Too often, such claims are based on conjecture or experience with other languages. + +There are examples where `final` can be important for both logical and performance reasons. +One example is a performance-critical AST hierarchy in a compiler or language analysis tool. +New derived classes are not added every year and only by library implementers. +However, misuses are (or at least has been) far more common. + +##### Enforcement + +Flag uses of `final`. ## C.hier-access: Accessing objects in a hierarchy @@ -7385,7 +7471,9 @@ Today, we might approximate that using `tie()`: This may be seen as an example of the *immediately initialize from input* exception below. -Creating optimal and equivalent code from all of these examples should be well within the capabilities of modern C++ compilers. +Creating optimal and equivalent code from all of these examples should be well within the capabilities of modern C++ compilers +(but don't make performance claims without measuring; a compiler may very well not generate optimal code for every example and +there may be language rules preventing some optimization that you would have liked in a particular case).. ##### Note @@ -12468,6 +12556,7 @@ Naming and layout rules: * [NL.16: Use a conventional class member declaration order](#Rl-order) * [NL.17: Use K&R-derived layout](#Rl-knr) * [NL.18: Use C++-style declarator layout](#Rl-ptr) +* [NL.25: Don't use `void` as an argument type](#Rl-void) Most of these rules are aesthetic and programmers hold strong opinions. IDEs also tend to have defaults and a range of alternatives.These rules are suggested defaults to follow unless you have reasons not to. @@ -12809,6 +12898,28 @@ The use in expressions argument doesn't hold for references. Impossible in the face of history. +### NL.25: Don't use `void` as an argument type + +##### Reason + +It's verbose and only needed where C compatibility matters. + +##### Example + + void f(void); // bad + + void g(); // better + +###### Note + +Even Dennis Ritchie deemed `void f(void)` an abomination. +You can make an argument for that abomination in C when function prototypes were rare so that banning + + int f(); + f(1,2,"weird but valid C89"); // hope that f() is defined int f(a,b,c) char* c; { /* ... */ } + +would have cause major problems, but not in the 21st century and in C++. + # FAQ: Answers to frequently asked questions This section covers answers to frequently asked questions about these guidelines.