{"id":17970358,"url":"https://github.com/muqsitnawaz/modern-cpp-cheatsheet","last_synced_at":"2025-05-08T21:17:47.974Z","repository":{"id":97866706,"uuid":"267765787","full_name":"muqsitnawaz/modern-cpp-cheatsheet","owner":"muqsitnawaz","description":"Best practices of Modern C++","archived":false,"fork":false,"pushed_at":"2020-10-06T08:07:31.000Z","size":11,"stargazers_count":261,"open_issues_count":1,"forks_count":19,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-05-08T21:17:34.180Z","etag":null,"topics":["best-practices","cheatsheet","cpp11","cpp14","modern-cpp"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/muqsitnawaz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-05-29T04:35:25.000Z","updated_at":"2025-03-27T16:29:33.000Z","dependencies_parsed_at":null,"dependency_job_id":"51bd38df-667d-4869-8196-44b5dba38cbd","html_url":"https://github.com/muqsitnawaz/modern-cpp-cheatsheet","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/muqsitnawaz%2Fmodern-cpp-cheatsheet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/muqsitnawaz%2Fmodern-cpp-cheatsheet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/muqsitnawaz%2Fmodern-cpp-cheatsheet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/muqsitnawaz%2Fmodern-cpp-cheatsheet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/muqsitnawaz","download_url":"https://codeload.github.com/muqsitnawaz/modern-cpp-cheatsheet/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253149622,"owners_count":21861740,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["best-practices","cheatsheet","cpp11","cpp14","modern-cpp"],"created_at":"2024-10-29T15:05:04.834Z","updated_at":"2025-05-08T21:17:47.893Z","avatar_url":"https://github.com/muqsitnawaz.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Effective Modern C++ Cheatsheet\n\n## Shorthands\n\n1. **ref(s)**: reference(s)\n2. **op(s)**: operation(s)\n\n## Terms\n\n1. **lvalue**: typically an expression whose address can be taken e.g a variable name (`auto x = 10;`)\n2. **rvalue**: an expression whose address cannot be taken in C++ i.e before C++11 e.g literal types (`10`)\n3. **lvalue-ref(erence)**: reference to an _lvalue_ type typically denoted by `\u0026` e.g `auto\u0026 lvalue_ref = x;`\n4. **rvalue-ref(erence)**: reference to an _rvalue_ type typically denoted by `\u0026\u0026` e.g `auto\u0026\u0026 rvalue_ref = 10;`\n5. **copy-operations**: _copy-construct_ from _lvalues_ using copy-constructor and copy-assignment operator\n6. **move-operations** _move-construct_ from _rvalues_ using move-constructor and move-assignment operator\n7. **arguments**: expressions passed to a function call at call site (could be either _lvalues_ or _rvalues_)\n8. **parameters**: _lvalue names_ initialized by arguments passed to a function e.g `x` in `void foo(int x);`\n9. **callable objects**: objects supporting member `operator()` e.g _functions_, `lambda`s, `std::function` etc\n10. **declarations**: introduce names and types without details e.g `class Widget;`, `void foo(int x);`\n11. **definitions**: provide implementation details e.g `class Widget { ... };`, `void foo(int x) { ... }`\n\n## Chapter 1. Deducing Types\n\n### Item 1: Understand `template` type deduction\n\n* Deduced type of T doesn't always match that of the parameter (i.e ParamType) in template functions\n* For _lvalue-refs/rvalue-refs_, compiler ignores the reference-ness of an _arg_ when deducing type of T\n* With _universal-refs_, type deduction always distinguishes between _l-value_ and _r-value_ argument types\n* With _pass-by-value_, _reference-ness_, `const` and `volatile` are ignored if present in the ParamType\n* Raw arrays `[]` and function types always decay to pointer types unless they initialize references\n\n### Item 2: Understand `auto` type deduction\n\n* `auto` plays the role of `T` while its type specifier (i.e including `const` and/or _ref_) as ParamType\n* For a braced initializer e.g `{1, 2, 3}`, `auto` _always_ deduces `std::initializer_list` as its type\n* **Corner case**: `auto` as a _callable_ `return` type uses _template type deduction_, not _auto type deduction_\n\n### Item 3: Understand `decltype`\n\n* `decltype`, typically used in function `template`s, determines a variable or an expression's type\n* `decltype(auto)`, unlike `auto`, includes _ref-ness_ when used in the `return` type of a _callable_\n* **Corner case**: `decltype` on _lvalue expression_ (except _lvalue-names_) yields _lvalue-refs_ not _lvalues_\n\n### Item 4: How to view deduced types?\n\n* You can update your code so that it leads to a compilation failure, you will see the type in diagnostics\n* `std::type_info::name` (and `typeid()`) depends upon compiler; use [Boost.TypeIndex](https://www.boost.org/doc/libs/1_66_0/doc/html/boost_typeindex/examples.html) library instead\n\n## Chapter 2. `auto`\n\n### Item 5: Prefer `auto` declarations\n\n* `auto` prevents _uninitialized variables_ and _verbose declarations_ (e.g `std::unordered_map\u003cT\u003e::key_type`)\n* Use `auto` especially when declaring _lambdas_ to directly hold _closures_ unlike `std::function`\n  \n### Item 6: How to fix undesired `auto` type deduction?\n\n* Use `auto` with `static_cast` (a.k.a _explicitly typed initializer idiom_) to enforce correct types\n* Never use `auto` directly with _invisible proxy classes_ such as `std::vector\u003cbool\u003e::reference` \n\n## Chapter 3. Moving to Modern C++\n\n### Item 7: Distinguish between () and {} (aka braced/uniform initializer) when creating objects\n\n* Braced initializer i.e `{}` prevents _narrowing conversions_ and _most vexing parse_ while `()` doesn't\n* During _overload-resolution_, `std::initializer_list` version is always preferred for `{}` types\n* **Corner case**: `std::vector\u003cint\u003e v{10, 20}` creates a vector with 10 and 20, not 10 `int`s initialized to 20.\n\n### Item 8: Prefer `nullptr` to `0` and `NULL`\n\n* Don't use `0` or `NULL`, use `nullptr` of type `nullptr_t` which represents pointers of all types!\n  \n### Item 9: Prefer alias declarations to `typedefs`\n\n* Alias declarations (declared with `using` keyword) support templatization while `typedefs` don't\n* Alias declarations avoid 1) `::type` suffix 2) `typename` prefix when referring to other typedefs\n\n### Item 10: Prefer _scoped_ `enums` to _unscoped_ `enums`\n\n* Use `enum class` instead of `enum` to limit scope of an `enum` members to just inside the `enum`\n* `enum class`es use `int` by default, prevent _implicit conversions_ and permit forward declarations\n\n### Item 11: Prefer public-deleted functions to private-undefined versions\n\n* Always make unwanted functions (such as _copy-operations_ for move-only types) `public` and `delete`\n\n### Item 12: Always declare overriding functions `override`\n\n* Declare overriding functions in derived types `override`; use `final` to prevent further inheritance\n\n### Item 13: Always prefer `const_iterators` to `iterators`\n\n* Prefer `const_iterators` to `iterators` for all STL containers e.g `cbegin` instead of `begin`\n* For max generic code, don't assume the existence of member `cbegin`; use `std::begin` instead\n\n### Item 14: Declare functions `noexcept` if they won't emit exceptions\n\n* Declare functions `noexcept` when they don't emit exceptions such as functions with _wide contracts_\n* Always use `noexcept` for move-operations, `swap` functions and memory allocation/deallocation\n* When a `noexcept` function emits an exception: stack is _possibly_ wound and program is terminated\n\n### Item 15: Use `constexpr` whenever possible\n\n* `constexpr` objects are always `const` and usable in _compile-time evaluations_ e.g `template` parameters\n* `constexpr` functions produce results at compile-time only if all of their args are known at compile-time\n* `constexpr` objects and functions can be used in a wider context i.e _compile-time_ as well as _runtime_\n\n### Item 16: Make `const` member functions thread-safe\n\n* Make member functions of a type `const` as well as `thread-safe` if they do not modify its members\n* For synchronization issues, consider `std::atomic` first and then move to `std::mutex` if required\n\n### Item 17: Understand when your compiler generates special member functions\n\n* Compiler generates a _default constructor_ only if the class type declares no _constructors_ at all\n* Declaring _destructor_ and/or _copy ops_ disables the generation of _default move ops_ and vice versa\n* _Copy assignment operator_ is generated if: 1) not already declared 2) no move op is declared\n\n## Chapter 4. Smart Pointers\n\n### Item 18: Use `std::unique_ptr` for exclusive-ownership of resource management\n\n* `std::unique_ptr` owns what it points to, is fast as raw pointer (`*`) and supports custom deleters\n* Conversion to a `std::shared_ptr` is easy, therefore _factory functions_ should always return `std::unique_ptr`\n* `std::array`, `std::vector` and `std::string` are generally better choices than using raw arrays `[]`\n\n### Item 19: Use `std::shared_ptr` for shared-ownership resource management\n\n* `std::shared_ptr` points to an object with _shared ownership_ but doesn't actually own the object\n* `std::shared_ptr` stores/updates _metadata_ on heap and can be up to 2x slower than `std::unique_ptr`\n* Unless you want custom deleters, prefer `std::make_shared\u003cT\u003e` for creating shared pointers\n* Don't create multiple `std::shared_ptr`s from a single raw pointer; it leads to _undefined behavior_\n* For `std::shared_ptr` to `this`, always inherit your class type from `std::enable_shared_from_this`\n\n### Item 20: Use `std::weak_ptr` for `std::shared_ptr`-like pointers that can dangle\n\n* `std::weak_ptr` operates with the possibility that the object it points to might have been destroyed\n* `std::weak_ptr::lock()` returns a `std::shared_ptr`, but a `nullptr` for _destroyed objects_ only\n* `std::weak_ptr` is typically used for _caching_, _observer lists_ and prevention of _shared pointers cycles_\n\n### Item 21: Prefer make functions (i.e `std::make_unique` and `std::make_shared`) to direct use of new\n\n* Use _make functions_ to remove source code duplication, improve exception safety and performance\n* When using `new` (in cases below), prevent memory leaks by immediately passing it to a _smart pointer_!\n* You must use `new` when 1) specifying custom deleters 2) pointed-to object is a _braced initializer_\n* Use `new` when `std::weak_ptr`s outlive their `std::shared_ptr`s to avoid _memory de-allocation delays_\n\n### Item 22: When using Pimpl idiom, define special member functions in an implementation file\n\n* _Pimpl idiom_ puts members of a type inside an impl type (`struct Impl`) and stores a pointer to it\n* Use `std::unique_ptr\u003cImpl\u003e` and always implement your destructor and _copy/move ops_  in an impl file\n\n## Chapter 5. Rvalue references, move semantics and perfect forwarding\n\n* _Move semantics_ aim to replace expensive _copy ops_ with the cheaper _move ops_ when applicable\n* _Perfect forwarding_ forwards a function's args to other functions parameters while preserving types\n\n### Item 23: Understand `std::move` and `std::forward`\n\n* `std::move` performs an unconditional cast on _lvalues_ to _rvalues_; you can then perform _move ops_\n* `std::forward` casts its _input arg_ to an _rvalue_ only if the _arg_ is bound to an _rvalue_ name\n\n### Item 24: Distinguish universal-refs from rvalue-refs\n\n* Universal-refs (i.e `T\u0026\u0026` and `auto\u0026\u0026`) always _cast_ lvalues to _lvalue-refs_ and rvalues to _rvalue-refs_\n* For universal-ref parameters, _auto/template type deduction_ must occur and they must be _non-`const`_\n\n### Item 25: Understand when to use `std::move` and `std::forward`\n\n* Universal references are usually a better choice than overloading functions for _lvalues_ and _rvalues_\n* Apply `std::move` on _rvalue refs_ and `std::forward` on _universal-refs_ last time each is used\n* Similarly, also apply `std::move` or `std::forward` accordingly when returning by value from functions\n* Never return _local objects_ from functions with `std::move`! It can prevent _return value optimization (RVO)_\n  \n### Item 26: Avoid overloading on universal-references\n\n* _Universal-refs_ should be used when client's code could pass either _lvalue refs_ or _rvalue refs_\n* Functions overloaded on _universal-refs_ typically get called more often than expected - avoid them!\n* Avoid _perf-forwarding constructors_ because they can hijack _copy/move ops_ for non-`const` types\n\n### Item 27: Alternatives to overloading universal-references\n\n* _Ref-to-const_ works but is less efficient while _pass-by-value_ works but use only for _copyable types_\n* _Tag dispatching_ uses an additional parameter type called _tag_ (e.g `std::is_integral`) to aid in matching\n* Templates using `std::enable_if_t` and `std::decay_t` work well for _universal-refs_ and they read nicely\n* _Universal-refs_ offer efficiency advantages although they sometimes suffer from usability disadvantages\n\n### Item 28: Understand reference collapsing\n\n* Reference collapsing converts `\u0026 \u0026\u0026` to `\u0026` (i.e lvalue ref) and `\u0026\u0026 \u0026\u0026` to `\u0026\u0026` (i.e rvalue ref)\n* Reference collapsing occurs in `template` and `auto` type deductions, alias declarations and `decltype`\n\n### Item 29: Assume that move operations are not present, not cheap, and not used\n\n* Generally, _moving_ objects is usually much cheaper then _copying_ them e.g _heap-based_ STL containers\n* For some types e.g `std::array` and `std::string` (with SSO), _copying_ them can be just as efficient\n\n### Item 30: Be aware of failure cases of perfect forwarding\n\n* _Perf-forwarding_ fails when _template type deduction_ fails or deduces wrong type for the arg passed\n* Fail cases: _braced initializers_ and passing `0` or `NULL` (instead of `nullptr`) for _null pointers_\n* For integral `static const` data members, _perfect-forwarding_ will fail if you're missing their definitions\n* For _overloaded_ or `template` functions, avoid _fail cases_ using `static_cast` to your desired type\n* Don't pass _bitfields_ directly to perfect-forwarding functions; use  `static_cast` to an _lvalue_ first\n\n## Chapter 6. Lambda Expressions\n\n### Item 31: Avoid default capture modes\n\n* Avoid default `\u0026` or `=` captures for _lambdas_ because they can easily lead to _dangling references_\n* Fail cases: `\u0026` when they outlive the objects captured, `=` for member types when they outlive `this` \n* `static` types are always captured _by-reference_ even though default capture mode could be _by-value_\n\n### Item 32: Use init-capture (aka generalized lambda captures) to move objects into (lambda) closures\n\n* _Init-capture_ allows you to initialize types (e.g variables) inside a lambda capture expression\n\n### Item 33: Use `decltype` on `auto\u0026\u0026` parameters for `std::forward`\n\n* Use `decltype` on `auto\u0026\u0026` parameters when using `std::forward` for forwarding them to other functions\n* This case will typically occur when you are implementing _perfect-forwarding_ using _auto type deduction_\n\n### Item 34: Prefer _lambdas_ to `std::bind`\n\n* Always prefer _init capture_ based `lambdas` (aka generalized lambdas) instead of using `std::bind`\n\n## Chapter 7. Concurrency API\n\n### Item 35: Prefer `std::async` (i.e task-based programming) to `std::thread` (i.e thread-based)\n\n* When using `std::thread`s, you almost always need to handle scheduling and oversubscription issues\n* Using `std::async` (aka task) with _default launch policy_ handles most of the corner cases for you\n  \n### Item 36: Specify `std::launch::async` for truly asynchronous tasks\n\n* `std::async`'s default launch policy can run either async (in new thread) or sync (upon `.get()` call)\n* If you get `std::future_status::deferred` on `.wait_for()`, call `.get()` to run the given task\n\n### Item 37: Always make `std::thread`s unjoinable on all paths\n\n* Avoid _program termination_ by calling `.join()` or `.detach()` on an `std::thread` before it _destructs_!\n* Calling `.join()` can lead to performance anomalies while `.detach()` leads to _undefined behavior_\n\n### Item 38: Be aware of varying destructor behavior of thread handle\n\n* `std::future` blocks in _destructor_ if policy is `std::launch::async` by calling an _implicit_ join\n* `std::shared_future` blocks when, additionally, the given shared future is the last copy in scope\n* `std::packaged_task` doesn't need a _destructor_ policy but the underlying `std::thread` (running it) does\n\n### Item 39: Consider `std::future`s of void type for one-shot communication (comm.)\n\n* For simple comm., `std::condition_variable`, `std::mutex` and `std::lock_guard` is an overkill\n* Use `std::future\u003cvoid\u003e` and `std::promise` for one-time communication between two threads\n\n### Item 40: Use `std::atomic` for concurrency and `volatile` for special memory\n\n* Use `std::atomic` guarantees _thread-safety_ for _shared memory_ while `volatile` specifies _special memory_\n* `std::atomic` prevents reordering of reads/write operations but permits elimination of _redundant reads/writes_\n* `volatile` specifies _special memory_ (e.g for _memory mapped variables_) which permits _redundant reads/writes_\n\n## Chapter 8. Tweaks\n\n### Item 41: When to use pass-by-value for functions parameters\n\n* Consider _pass-by-value_ for parameters if and only if they are always copied and are cheap to move\n* Prefer `rvalue-ref` parameters for move-only types to limit copying to exactly one move operation\n* Never use _pass-by-value_ for base class parameter types because it leads to the _slicing problem_\n\n### Item 42: Choose emplacement instead of insertion\n\n* Use `.emplace` versions instead of `.push/.insert` to avoid temp copies when adding to STL containers\n* When value being added uses assignment, `.push/.insert` work just as well as the `.emplace` versions\n* For containers of _resource-managing types_ e.g smart pointers, `.push/.insert` can prevent memory leaks\n* Be careful when using `.emplace` functions because the _args_ passed can invoke explicit _constructors_\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmuqsitnawaz%2Fmodern-cpp-cheatsheet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmuqsitnawaz%2Fmodern-cpp-cheatsheet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmuqsitnawaz%2Fmodern-cpp-cheatsheet/lists"}