---
description: "CodeWiki C++ pitfalls and review checks"
globs: ["**/*.cpp","**/*.cc","**/*.cxx","**/*.h","**/*.hpp"]
alwaysApply: false
---

# C++ rules

Apply these rules when a matching file is in context.

- Multiplication in `malloc(count sizeof items)` is evaluated as `size_t` before the call.
  Why: Comparing the result with `SIZE_MAX` is too late because an unsigned value may already have wrapped to a small number.
  Source: [C dynamic memory management](https://codewiki.com/cpp/c-memory/)
- Do not assume this is safe: `items = realloc(items, bytes)` overwrites the sole owning pointer with null on failure and causes a memory leak.
  Why: On success, every old alias is invalid too.
  Source: [C dynamic memory management](https://codewiki.com/cpp/c-memory/)
- `free(owner); owner = NULL;` changes only the `owner` variable.
  Why: Other pointer copies remain dangling pointers, and testing them against `NULL` cannot reveal that the lifetime has ended.
  Source: [C dynamic memory management](https://codewiki.com/cpp/c-memory/)
- Do not assume this is safe: `calloc` guarantees all bits zero, not the semantic zero value of every type.
  Why: It also cannot create nested ownership or allocate each pointer member of a structure.
  Source: [C dynamic memory management](https://codewiki.com/cpp/c-memory/)
- A non-null pointer proves only that one byte request succeeded.
  Why: It does not prove that a caller's `count` is correct or that an index lies within the block. A wrong element type or length can still cause an out-of-bounds access.
  Source: [C dynamic memory management](https://codewiki.com/cpp/c-memory/)
- Do not assume this is safe: using `while (!feof(stream))` to predict the end processes stale buffer contents after the final read fails.
  Why: Storing an `fgetc()` result in `char` can also mistake a valid byte for `EOF`.
  Source: [C file I/O](https://codewiki.com/cpp/c-file-io/)
- Do not assume this is safe: generated code often checks only `fopen()` and ignores `fprintf()`, `fwrite()`, `fflush()`, and `fclose()`.
  Why: If storage fills or a delayed low-level write fails, the function still reports a successful save.
  Source: [C file I/O](https://codewiki.com/cpp/c-file-io/)
- `w` and `w+` truncate an existing file as soon as it is successfully opened.
  Why: Passing an unauthorized or unnormalized path directly to a generated save function can also overwrite data outside the application's boundary.
  Source: [C file I/O](https://codewiki.com/cpp/c-file-io/)
- Do not assume this is safe: switching directly from writing to reading on an `r+`, `w+`, or `a+` stream, or from a read that did not reach EOF to writing, violates update-stream sequencing rules.
  Why: Small-file tests can appear to work because of a particular buffer layout.
  Source: [C file I/O](https://codewiki.com/cpp/c-file-io/)
- `fwrite(&record, sizeof record, 1, stream)` writes the current implementation's object representation, which can include padding, host byte order, and implementation-specific type widths.
  Why: It is not an automatically portable file format and must not be trusted and used to allocate memory without validation.
  Source: [C file I/O](https://codewiki.com/cpp/c-file-io/)
- Generated or legacy code may cast `double ()(double)` to `int ()(int)` only because the compiler rejected the original assignment.
  Why: The cast changes the static type, not how the target function actually receives arguments or returns a result.
  Source: [C function pointers](https://codewiki.com/cpp/c-function-pointers/)
- Do not assume this is safe: the checks in `if (handlers[index] != NULL && index < count)` are in the wrong order.
  Why: With an invalid index, the left operand has already read out of bounds before the right operand can protect it.
  Source: [C function pointers](https://codewiki.com/cpp/c-function-pointers/)
- `return left->key - right->key` appears to produce negative, zero, and positive results, but two distant `int` values can cause signed overflow.
  Why: The algorithm then receives undefined behavior rather than a reliable ordering.
  Source: [C function pointers](https://codewiki.com/cpp/c-function-pointers/)
- A registration function may retain `&local_state`, then invoke the callback only after the registration function returns.
  Why: The function pointer remains valid, but the `void *` points to an object whose lifetime has ended.
  Source: [C function pointers](https://codewiki.com/cpp/c-function-pointers/)
- Do not assume this is safe: `void ` is a generic object pointer, not standard C's generic function-pointer container.
  Why: Converting a function pointer to `void ` and later calling it depends on implementation or platform extensions; equal sizes on one machine do not make the pattern portable.
  Source: [C function pointers](https://codewiki.com/cpp/c-function-pointers/)
- If one thread rewrites a global handler while another thread reads or calls it, conflicting accesses to the ordinary function-pointer object form a data race.
  Why: Copying it to a local variable only narrows later use; it does not make the initial unsynchronized read safe.
  Source: [C function pointers](https://codewiki.com/cpp/c-function-pointers/)
- Do not assume this is safe: a matching signature does not imply matching semantics.
  Why: A comparator that mutates elements, a logging callback that recursively logs, or a cleanup callback invoked twice can all break invariants maintained by the caller.
  Source: [C function pointers](https://codewiki.com/cpp/c-function-pointers/)
- Do not assume this is safe: `int result;` declares an object but does not guarantee a zero value.
  Why: If a control-flow path reads `result` before assigning it, the program can invoke undefined behavior.
  Source: [C fundamentals](https://codewiki.com/cpp/c-basics/)
- Do not assume this is safe: writing an `int` into a fixed four-byte protocol or assuming plain `char` is signed turns a property of one platform into a supposed C contract.
  Source: [C fundamentals](https://codewiki.com/cpp/c-basics/)
- When a `-1` error sentinel is compared with `size_t`, the negative value may convert to a large unsigned value and reverse an apparently obvious ordering.
  Source: [C fundamentals](https://codewiki.com/cpp/c-basics/)
- Do not assume this is safe: variadic arguments do not carry enough information for `printf` to correct a type.
  Why: Reading a `long` with `%d`, an `int` with `%f`, or a `size_t` without `%zu` can all produce undefined behavior.
  Source: [C fundamentals](https://codewiki.com/cpp/c-basics/)
- Do not assume this is safe: `values[index] = index++;` leaves the read and modification of `index` without a required ordering.
  Why: Precedence can explain how the expression groups, but it does not sequence these side effects.
  Source: [C fundamentals](https://codewiki.com/cpp/c-basics/)
- An ordinary local object's lifetime ends when the function returns.
  Why: An address returned as `&local` no longer points to that accessible object, even if its numeric value appears unchanged.
  Source: [C pointers](https://codewiki.com/cpp/c-pointers/)
- `if (pointer != NULL)` cannot detect dangling, out-of-bounds, misaligned, or wrongly typed pointers.
  Why: An alias commonly retains a nonzero bit pattern after release.
  Source: [C pointers](https://codewiki.com/cpp/c-pointers/)
- Do not assume this is safe: `sizeof pointer / sizeof pointer[0]` inside a function uses the pointer object's size, not the caller's array length.
  Why: A parameter written as `int values[8]` has likewise already been adjusted to a pointer.
  Source: [C pointers](https://codewiki.com/cpp/c-pointers/)
- Casting `const int ` to `int ` doesn't make an actually read-only object writable.
  Why: Casting an arbitrary byte address to a structure pointer doesn't automatically satisfy alignment, size, or object-access rules either.
  Source: [C pointers](https://codewiki.com/cpp/c-pointers/)
- `free(owner); owner = NULL;` doesn't change an address copied earlier into `cached`.
  Why: Checking or using `cached` can still hit a dangling-pointer bug, and releasing it again violates the allocator contract.
  Source: [C pointers](https://codewiki.com/cpp/c-pointers/)
- Parentheses fix precedence in `#define MAX(a, b) ((a) > (b) ?
  Why: (a) : (b))`, but the winning argument may still be evaluated twice. `MAX(index++, limit)` can increment `index` twice; other replacement lists can put repeated modifications in unsequenced operands and cause undefined behavior.
  Source: [C preprocessor](https://codewiki.com/cpp/c-preprocessor/)
- After `#define FEATURE_X 0`, `#ifdef FEATURE_X` is still true.
  Why: Generated code often mixes presence tests with Boolean value tests, compiling an enabled path into a disabled configuration.
  Source: [C preprocessor](https://codewiki.com/cpp/c-preprocessor/)
- A bare `{ ...
  Why: }` block or several unwrapped statements can change which `if` owns an `else` when the caller omits braces. Putting a semicolon in the macro definition also creates an empty statement and can be a syntax error in some control-flow positions.
  Source: [C preprocessor](https://codewiki.com/cpp/c-preprocessor/)
- Macros don't obey C block scope or namespaces.
  Why: Short names, reserved forms that begin with underscores, and duplicate include-guard names can rewrite unrelated code or silently suppress a complete header.
  Source: [C preprocessor](https://codewiki.com/cpp/c-preprocessor/)
- Do not assume this is safe: `typeof`, statement expressions, `, ##__VA_ARGS__`, and vendor-specific pragmas may work in the current compiler without belonging to the target C23 interface.
  Why: Copying these forms from older projects without naming the dialect hides the portability constraint.
  Source: [C preprocessor](https://codewiki.com/cpp/c-preprocessor/)
- Do not treat struct size as the sum of member sizes; doing so ignores internal or tail padding; presenting one machine's result as a language guarantee is equally wrong.
  Source: [C structs, unions and enums](https://codewiki.com/cpp/c-structs-unions/)
- Do not assume this is safe: changing a union payload without synchronizing its discriminant makes a reader interpret storage as the wrong type; changing only the tag without constructing the new payload has the same defect.
  Source: [C structs, unions and enums](https://codewiki.com/cpp/c-structs-unions/)
- Using a union, bit fields, a `packed` attribute, or `fwrite(&record, sizeof record, 1, file)` to define a wire or disk format turns compiler layout and host byte order into an implicit protocol.
  Source: [C structs, unions and enums](https://codewiki.com/cpp/c-structs-unions/)
- Ordinary struct assignment shallow-copies owning pointers.
  Why: Two copies may later free the same address, or one may release it and leave the other with a dangling pointer.
  Source: [C structs, unions and enums](https://codewiki.com/cpp/c-structs-unions/)
- Do not assume this is safe: when an enum object originates as an untrusted integer, a `switch` cannot assume it equals one of the enumerators.
  Why: Array indexes, function-table indexes, and union dispatch are especially vulnerable to out-of-range values or wrong-member reads.
  Source: [C structs, unions and enums](https://codewiki.com/cpp/c-structs-unions/)
- Comparing two structs with `memcmp` includes padding bytes.
  Why: Even when corresponding members compare equal, padding can hold different unspecified values.
  Source: [C structs, unions and enums](https://codewiki.com/cpp/c-structs-unions/)
- A loop condition written as `index undefined behavior.
  Why: Looking correct in one test run is not evidence that the program is valid.
  Source: [C-style arrays](https://codewiki.com/cpp/c-arrays/)
- In `void inspect(int values[8])`, `values` is a pointer.
  Why: `sizeof(values) / sizeof(values[0])` divides the pointer size by the element size; it does not produce the caller's array extent.
  Source: [C-style arrays](https://codewiki.com/cpp/c-arrays/)
- `auto backup = readings` deduces a pointer and copies no elements; `int backup[4] = readings` is ill-formed.
  Why: An array is not an ordinary assignable value type.
  Source: [C-style arrays](https://codewiki.com/cpp/c-arrays/)
- Returning a pointer, reference, iterator, or span into a local array leaves a dangling view when the function returns.
  Why: A span carries a length, but it neither owns elements nor extends their lifetime.
  Source: [C-style arrays](https://codewiki.com/cpp/c-arrays/)
- Do not assume this is safe: `char code[3]{'A', 'B', 'C'}` contains three characters but is not a C string.
  Why: Passing it to a function that expects null termination makes that function read beyond the array while searching for `\0`.
  Source: [C-style arrays](https://codewiki.com/cpp/c-arrays/)
- Making every data member public, or mechanically generating a setter for each one, lets callers assemble states the class was meant to reject.
  Why: If a range type exposes separate `set_min()` and `set_max()` calls, an intermediate step can produce `min > max`.
  Source: [Classes](https://codewiki.com/cpp/classes/)
- Do not assume this is safe: the order written in an initializer list does not change the real member initialization order.
  Why: If an earlier-declared member uses a later-declared member's value, the latter is not initialized yet; reading an uninitialized scalar can cause undefined behavior.
  Source: [Classes](https://codewiki.com/cpp/classes/)
- `member = value` in a constructor body is assignment.
  Why: Before the body starts, `member` was already default-initialized, or the program already failed to compile because a reference, `const` member, or member without a default constructor could not be initialized.
  Source: [Classes](https://codewiki.com/cpp/classes/)
- A constructor callable with one argument may become a converting constructor.
  Why: Generated code often omits `explicit`, so a call that requires a strong type also accepts an integer or string, and overload selection becomes harder to see.
  Source: [Classes](https://codewiki.com/cpp/classes/)
- A compiler-generated copy copies the value of each member.
  Why: If a raw pointer represents exclusive ownership, two objects receive the same address and may later release it twice; adding only a destructor does not repair copy semantics.
  Source: [Classes](https://codewiki.com/cpp/classes/)
- Do not assume this is safe: a read-only member function without trailing `const` cannot be called through a `const` object or reference.
  Why: Generated getters often miss it, forcing callers to discard a qualifier that should remain part of the contract.
  Source: [Classes](https://codewiki.com/cpp/classes/)
- `throw new Error` mixes exception delivery with pointer ownership, while `catch (std::exception error)` copies only the base part and slices away the derived type.
  Source: [Exceptions](https://codewiki.com/cpp/exceptions/)
- Generated code often adds `catch (...) {}` or logs and continues even though the current function has neither restored its state nor produced a valid result.
  Source: [Exceptions](https://codewiki.com/cpp/exceptions/)
- Do not assume this is safe: releasing a `new` allocation, lock, or handle manually at the end of a function skips cleanup when an intervening statement throws.
  Why: Adding `try`/`catch` cleanup around every call easily misses new exit paths.
  Source: [Exceptions](https://codewiki.com/cpp/exceptions/)
- A function changes several members and then runs validation, allocation, or a callback that can throw.
  Why: It leaves partially committed state while claiming the strong exception guarantee.
  Source: [Exceptions](https://codewiki.com/cpp/exceptions/)
- A model marks every destructor, move operation, or small wrapper `noexcept` while its body allocates, formats a log message, invokes a callback, or calls some other potentially throwing code.
  Source: [Exceptions](https://codewiki.com/cpp/exceptions/)
- Do not treat out-of-range, not-found, or temporarily-empty states as loop termination hides the contract and sends every normal completion through the exceptional path.
  Source: [Exceptions](https://codewiki.com/cpp/exceptions/)
- Building public inheritance only to reuse implementation can expose a base contract that the derived class cannot honestly support.
  Source: [Inheritance](https://codewiki.com/cpp/inheritance/)
- Passing or returning a polymorphic base by value, or putting it in a base-value container, silently removes derived state.
  Source: [Inheritance](https://codewiki.com/cpp/inheritance/)
- A same-named function in a derived class hides base overloads even when their parameter lists differ.
  Why: A call may select an unintended conversion or stop compiling.
  Source: [Inheritance](https://codewiki.com/cpp/inheritance/)
- Do not assume this is safe: deleting a derived object through a base pointer has undefined behavior when the base destructor is not virtual.
  Source: [Inheritance](https://codewiki.com/cpp/inheritance/)
- Do not assume this is safe: a virtual call in a base constructor or destructor does not dispatch to a derived part that has not begun construction or has already finished destruction.
  Source: [Inheritance](https://codewiki.com/cpp/inheritance/)
- A diamond hierarchy contains two common-base subobjects by default, which can make member access and upcasts ambiguous.
  Source: [Inheritance](https://codewiki.com/cpp/inheritance/)
- Generated and handwritten code often asserts `source.empty()` after a move or keeps reading the source through old indices.
  Why: Valid but unspecified does not mean guaranteed empty.
  Source: [Move semantics](https://codewiki.com/cpp/move-semantics/)
- `std::move(const_value)` preserves `const`, commonly producing `const T&&`.
  Why: The usual move constructor `T(T&&)` cannot bind to it, so a copy overload may be selected.
  Source: [Move semantics](https://codewiki.com/cpp/move-semantics/)
- `return std::move(result);` changes the name expression into an xvalue and can prevent named return value optimization (NRVO).
  Why: It is usually worse than a direct return.
  Source: [Move semantics](https://codewiki.com/cpp/move-semantics/)
- Do not assume this is safe: without `noexcept` on a custom move operation, containers and `std::move_if_noexcept` may choose to copy.
  Why: A false `noexcept` promise turns an ordinary exception into program termination.
  Source: [Move semantics](https://codewiki.com/cpp/move-semantics/)
- A model can easily generate a raw-pointer move constructor while forgetting to delete copying, release the target's old resource during move assignment, move a base class, or reset the length paired with a pointer.
  Source: [Move semantics](https://codewiki.com/cpp/move-semantics/)
- `T&&` is a forwarding reference only in specific contexts such as template argument deduction.
  Why: Applying `std::move` unconditionally to that named parameter treats even an lvalue supplied by the caller as consumable.
  Source: [Move semantics](https://codewiki.com/cpp/move-semantics/)
- A directive such as `using namespace std;` in a header affects unqualified lookup after every inclusion site.
  Why: Adding a library overload or including another header can make an existing call ambiguous, while the error appears in the includer's code.
  Source: [Namespaces](https://codewiki.com/cpp/namespaces/)
- Do not assume this is safe: `library::detail` is a naming convention, not a private region.
  Why: Generated code sometimes puts sensitive state in a named `detail` namespace and assumes callers can't access or modify it.
  Source: [Namespaces](https://codewiki.com/cpp/namespaces/)
- An unnamed namespace variable in a header creates an independent entity in every translation unit.
  Why: A counter incremented by one source file may never change the counter seen by another, even though both came from identical source text.
  Source: [Namespaces](https://codewiki.com/cpp/namespaces/)
- Adding functions, types, or overloads to `std` usually gives the program undefined behavior.
  Why: The standard permits only a small set of customizations under specific conditions, often specializations involving program-defined types; that isn't a general extension mechanism.
  Source: [Namespaces](https://codewiki.com/cpp/namespaces/)
- Generic code that directly calls `std::swap(a, b)` can bypass a customization overload next to an argument's type.
  Why: At the other extreme, relying on unqualified lookup for every business call makes candidate sets harder to predict.
  Source: [Namespaces](https://codewiki.com/cpp/namespaces/)
- A header declares `acme::load()`, but a generated source file defines global `load()` or puts it in `acme::detail`.
  Why: Both functions can compile separately; calling the declaration produces an undefined reference only when linking.
  Source: [Namespaces](https://codewiki.com/cpp/namespaces/)
- To avoid writing a copy, generated code sometimes makes `operator+` change `*this`.
  Why: Then `a + b` changes `a`, violating the usual arithmetic and value-semantics contract, and evaluating the same expression twice can produce different results.
  Source: [Operator overloading](https://codewiki.com/cpp/operator-overloading/)
- A binary operator that returns a reference to a local result leaves a dangling reference as soon as the function ends.
  Why: A compound assignment returning by value may compile, but `(a += b) += c` then mutates a temporary copy instead of continuing to mutate `a`.
  Source: [Operator overloading](https://codewiki.com/cpp/operator-overloading/)
- A member `Quantity::operator(double)` can support `quantity 2.0`, but it can't automatically support `2.0 * quantity`.
  Why: Compiling only the first call does not establish that the interface is commutative.
  Source: [Operator overloading](https://codewiki.com/cpp/operator-overloading/)
- A defaulted `operator` compares base subobjects and non-static data members in declaration order.
  Why: If the object also stores a cache, database surrogate key, or debug counter, defaulting can turn implementation state into value identity. A floating member can also produce a partial order with `unordered` results.
  Source: [Operator overloading](https://codewiki.com/cpp/operator-overloading/)
- An overloaded `operator&&` or `operator||` is a function call whose arguments both need evaluation.
  Why: Code that relies on the right side not running to avoid a null dereference, expensive work, or a side effect breaks when the operand type introduces an overload.
  Source: [Operator overloading](https://codewiki.com/cpp/operator-overloading/)
- Several single-argument constructors plus several conversion operators give one expression multiple plausible routes.
  Why: Adding an overload later can make an old call ambiguous or silently select a different function.
  Source: [Operator overloading](https://codewiki.com/cpp/operator-overloading/)
- A lone `operator[]` often has one of two holes: returning by value makes the element unwritable, while a non-`const` member leaves read-only objects unreadable.
  Why: Another failure is implementing unchecked access while documentation promises an out-of-range exception.
  Source: [Operator overloading](https://codewiki.com/cpp/operator-overloading/)
- A raw handle followed by one trailing `close()` covers only the straight-line success path.
  Why: A new early return, validation exception, or intermediate allocation failure can bypass it.
  Source: [RAII](https://codewiki.com/cpp/raii/)
- If a class with a raw pointer or integer handle keeps compiler-generated copying, two objects believe they must release the same resource.
  Why: The result is usually double release, dangling access, or a leak introduced to avoid the crash.
  Source: [RAII](https://codewiki.com/cpp/raii/)
- A function named `get()` usually lends a handle.
  Why: If the caller stores it, closes it, or gives it to another owner, the original object still releases it according to its own contract, causing a dangling handle or double release.
  Source: [RAII](https://codewiki.com/cpp/raii/)
- Do not assume this is safe: closing a file, submitting telemetry, or releasing a remote lease can itself fail.
  Why: Callers can't reliably handle an exception escaping a destructor; if stack unwinding is already in progress, the program terminates.
  Source: [RAII](https://codewiki.com/cpp/raii/)
- RAII releases memory and handles, but it doesn't automatically undo data appended to a container, a message already sent, or a record written to an external system.
  Why: No resource leak doesn't mean the operation has the strong exception guarantee.
  Source: [RAII](https://codewiki.com/cpp/raii/)
- A dynamically allocated owner isn't destroyed merely because the block that created it ends.
  Why: `std::exit()`, `std::_Exit()`, `std::abort()`, and process crashes don't follow ordinary local stack-unwinding paths either.
  Source: [RAII](https://codewiki.com/cpp/raii/)
- Do not assume this is safe: `current = replacement` does not make a reference designate `replacement`.
  Why: It invokes assignment on the bound object and may change much of that object's state.
  Source: [References](https://codewiki.com/cpp/references/)
- An object with automatic storage duration is destroyed when its function exits.
  Why: Returning `T&` or `const T&` to it does not carry the object out of the scope; returning a reference to a temporary made by a computed expression has the same class of problem.
  Source: [References](https://codewiki.com/cpp/references/)
- Do not assume this is safe: `const T&` can extend the lifetime of some directly bound temporary objects, but it does not extend the lifetime of an ordinary lvalue, a pointer's target, or an object behind another short-lived reference.
  Why: `const` also means neither thread safety nor deep immutability.
  Source: [References](https://codewiki.com/cpp/references/)
- `T&&` is a forwarding reference only when template deduction occurs there and the form meets the deduction rule.
  Why: A concrete `Widget&&`, a `const T&&`, and most `T&&` members of class templates cannot bind lvalues in the same way.
  Source: [References](https://codewiki.com/cpp/references/)
- Do not assume this is safe: for cheaply copied types such as integers, a reference adds indirection without expressing useful semantics.
  Why: For a function that retains a copy, accepting only `const&` can also force the implementation to copy every time.
  Source: [References](https://codewiki.com/cpp/references/)
- A reference obtained from a container follows that container's invalidation rules.
  Why: Vector reallocation, element removal, or destruction of the whole object can leave old references dangling, with no detectable null state in the reference syntax.
  Source: [References](https://codewiki.com/cpp/references/)
- Mechanically replacing every `unique_ptr` or raw borrow with `shared_ptr` hides the place responsible for release.
  Why: Once copying becomes ubiquitous, objects may be destroyed far later than expected and reference cycles become easier to create.
  Source: [Smart pointers](https://codewiki.com/cpp/smart-pointers/)
- Evaluating `std::shared_ptr(raw)` twice usually creates two control blocks that know nothing about each other.
  Why: Each eventually deletes the same object; constructing a `shared_ptr` directly from `this` creates the same class of bug.
  Source: [Smart pointers](https://codewiki.com/cpp/smart-pointers/)
- Do not assume this is safe: reference counting sees pointer counts, not whether a group of objects is unreachable from program roots.
  Why: Two nodes holding `shared_ptr` values to each other, or an object storing a callback that strongly captures itself, prevent the strong counts from reaching zero.
  Source: [Smart pointers](https://codewiki.com/cpp/smart-pointers/)
- A `false` result from `expired()` describes only the instant of the check; another thread or callback can immediately release the last strong owner.
  Why: Checking first and then relying on an earlier raw pointer can still dangle.
  Source: [Smart pointers](https://codewiki.com/cpp/smart-pointers/)
- Do not assume this is safe: `get()` lends an address without transferring release responsibility; storing that result long-term or giving it to another owner causes dangling access or double deletion.
  Why: `unique_ptr::release()` disables automatic deletion, so it leaks if no recipient adopts the result immediately.
  Source: [Smart pointers](https://codewiki.com/cpp/smart-pointers/)
- Different `shared_ptr` objects sharing one control block can be copied and destroyed independently across threads, but that doesn't make the managed `T` thread-safe.
  Why: Concurrent non-read-only operations on the same `shared_ptr` variable also require synchronization.
  Source: [Smart pointers](https://codewiki.com/cpp/smart-pointers/)
- The base declares `virtual void save() const`, but generated code writes `void save()`.
  Why: The missing `const` makes this a different function. Without `override`, the code may compile while calls through the base interface still use the old implementation.
  Source: [Virtual functions](https://codewiki.com/cpp/virtual-functions/)
- A factory returns `std::unique_ptr`, but `Base::~Base()` is public and non-virtual.
  Why: A smart pointer can't repair the class contract: it still deletes the derived object through `Base*`, which has undefined behavior.
  Source: [Virtual functions](https://codewiki.com/cpp/virtual-functions/)
- A parameter, return value, data member, or `std::vector` that stores a polymorphic base by value slices the derived part during copying.
  Why: Later virtual calls target the new `Base` object and can't recover its former dynamic type.
  Source: [Virtual functions](https://codewiki.com/cpp/virtual-functions/)
- A base constructor calls virtual `configure()` and expects the derived class to populate state.
  Why: The call doesn't dispatch to the derived implementation. If the base declares that function pure virtual and makes a virtual call to it, the program can enter undefined behavior.
  Source: [Virtual functions](https://codewiki.com/cpp/virtual-functions/)
- The function body is chosen from the dynamic type, but a default argument comes from the call expression's static type.
  Why: Calls through `Base&` and `Derived&` can enter the same override with different default values, making results depend on the call-site type.
  Source: [Virtual functions](https://codewiki.com/cpp/virtual-functions/)
