# Morden C++ Best Practices and Low-Level Operations

This guide turns a broad set of modern C++ topics into a practical reference. It focuses on two things:

1. **How the feature works**
2. **How to use it well in production code**

The goal is not to memorize every rule, but to build good instincts: prefer clarity, rely on the type system, and understand the language rules that matter for performance and correctness.

***

## 1. Variadic Templates and Parameter Packs

Variadic templates let a template accept any number of type or value arguments. The pack can then be expanded into a function call, initializer list, or expression.

### Why they matter

* They are the foundation of generic wrappers, tuple utilities, forwarding helpers, and logging APIs.
* They replace preprocessor tricks and fixed-arity overload sets.

### Best practices

* Prefer **fold expressions** in C++17+ instead of recursive pack expansion.
* Forward packs with `std::forward<Args>(args)...` when preserving value category matters.
* Use `sizeof...(Args)` when code should behave differently for an empty or non-empty pack.

### Example

```cpp
template <typename... Args>
auto sum(Args... args) {
    return (args + ...);
}

template <typename F, typename... Args>
decltype(auto) invoke(F&& f, Args&&... args) {
    return std::forward<F>(f)(std::forward<Args>(args)...);
}
```

### Mental model

```
Right fold:

(args + ...) with args = 1, 2, 3
=> 1 + (2 + 3)
```

***

## 2. Move Semantics and Perfect Forwarding

Move semantics let an object transfer ownership of its resources instead of copying them. Perfect forwarding lets a wrapper preserve whether an argument was an lvalue or an rvalue.

### Key distinction

* `std::move` does **not** move anything by itself. It only casts to an rvalue.
* `std::forward` is only correct for forwarding references in a deduced context.

### Best practices

* Mark move constructor and move assignment `noexcept` when that is true.
* Use `std::move` only when you are done with the source object.
* Return by value freely; copy elision and moves are usually efficient.
* In generic code, prefer forwarding references over manually duplicating overloads.

### Example

```cpp
class Buffer {
public:
    Buffer() = default;
    explicit Buffer(std::size_t n) : size_(n), data_(std::make_unique<char[]>(n)) {}

    Buffer(Buffer&& other) noexcept
        : size_(other.size_), data_(std::move(other.data_)) {
        other.size_ = 0;
    }

    Buffer& operator=(Buffer&& other) noexcept {
        if (this != &other) {
            size_ = other.size_;
            data_ = std::move(other.data_);
            other.size_ = 0;
        }
        return *this;
    }

private:
    std::size_t size_ = 0;
    std::unique_ptr<char[]> data_;
};

template <typename T, typename... Args>
T make_object(Args&&... args) {
    return T(std::forward<Args>(args)...);
}
```

### Rule of thumb

Use move semantics for ownership transfer. Use perfect forwarding when writing a pass-through abstraction.

***

## 3. Concepts and SFINAE

Both concepts and SFINAE constrain templates, but concepts are the modern tool.

### Concepts

Concepts are named compile-time predicates. They make interfaces explicit and error messages much easier to understand.

### SFINAE

SFINAE ("Substitution Failure Is Not An Error") is the older style, usually built with `std::enable_if`, `decltype`, or `void_t`.

### Best practices

* Prefer **concepts** for new code.
* Use `requires` clauses to document expectations at the call site.
* Keep SFINAE only when maintaining older code or supporting older standards.

### Example

```cpp
#include <concepts>

template <typename T>
concept Incrementable = requires(T x) {
    ++x;
    x++;
};

template <Incrementable T>
void increment(T& x) {
    ++x;
}
```

### Example using SFINAE

```cpp
#include <type_traits>

template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
void increment_if_integral(T& x) {
    ++x;
}
```

### Why concepts are better

With SFINAE, an invalid overload silently disappears. With concepts, the compiler tells you directly which requirement failed.

***

## 4. CRTP (Curiously Recurring Template Pattern)

CRTP is a pattern where a base class template takes the derived class as a template parameter.

```cpp
template <typename Derived>
class Addable {
public:
    friend Derived operator+(Derived lhs, const Derived& rhs) {
        lhs += rhs;
        return lhs;
    }
};

class Vec2 : public Addable<Vec2> {
public:
    Vec2(int x, int y) : x_(x), y_(y) {}

    Vec2& operator+=(const Vec2& other) {
        x_ += other.x_;
        y_ += other.y_;
        return *this;
    }

private:
    int x_;
    int y_;
};
```

### When it is useful

* Static polymorphism
* Mixins
* Zero-overhead interfaces when the concrete type is known at compile time

### Best practices

* Use CRTP when you want compile-time composition, not runtime substitution.
* Access derived functionality through `static_cast<Derived&>(*this)` when needed.
* Avoid CRTP when ordinary free functions or composition are simpler.

***

## 5. Traits, Policies, and Tag Types

These are classic tools for configuring generic code.

### Traits

Traits expose compile-time properties of a type.

```cpp
std::is_integral_v<int>   // true
std::is_pointer_v<int*>   // true
```

### Policies

A policy is a template parameter that customizes behavior.

```cpp
template <typename T, typename Alloc = std::allocator<T>>
class SimpleVector;
```

A policy is a template parameter that changes **how** a generic type or algorithm behaves without changing its overall purpose.

In practice, a policy is usually a small type that provides one specific strategy, such as:

* memory allocation
* error handling
* locking
* sorting or comparison rules

This is useful when the main template should stay generic, but one part of its behavior should remain configurable at compile time.

A classic example is an allocator policy:

```
template <typename T, typename Alloc = std::allocator<T>>
class SimpleVector;
```

Here, `SimpleVector<T, Alloc>` is still a vector-like container, but the `Alloc` policy decides how memory is obtained and released.

Another example is a policy-based logger:

```
struct ConsoleWriter {
    void write(const std::string& msg) const {
        std::cout << msg << '\n';
    }
};

struct SilentWriter {
    void write(const std::string& msg) const {
    }
};

template <typename WriterPolicy>
class Logger : private WriterPolicy {
public:
    void log(const std::string& msg) {
        this->write(msg);
    }
};

Logger<ConsoleWriter> visible;
visible.log("hello");

Logger<SilentWriter> quiet;
quiet.log("ignored");
```

In this design, the `Logger` type stays the same, but the output behavior changes through the policy type.

A good policy should be:

* focused on one responsibility
* easy to replace
* documented by a clear interface
* chosen when compile-time customization is preferable to runtime branching

### Tag types

Tag types select behavior by overload resolution.

```cpp
struct fast_tag {};
struct safe_tag {};

template <typename T>
void process(T& value, fast_tag) {
    // Fast path with fewer checks.
}

template <typename T>
void process(T& value, safe_tag) {
    // Slower path with more validation.
}

template <typename Tag, typename T>
void run_with(T& value) {
    process(value, Tag{});
}

int value = 42;
run_with<safe_tag>(value);
```

### Overloading Universal Reference with Tag Dispatch

`Universal reference` is the older informal name for a **forwarding reference**: `T&&` where `T` is deduced.

`Tag dispatch` means choosing an implementation by passing a small type, such as `std::true_type`, `std::false_type`, or a custom tag.

Combined, the pattern is:

1. accept the argument as `T&&`
2. preserve its value category with `std::forward<T>(value)`
3. dispatch to the right helper with a tag

This is useful when one API should accept both lvalues and rvalues, but the internal behavior should differ.

#### Example

```cpp
#include <iostream>
#include <type_traits>
#include <utility>

template <typename T>
void store_impl(T&& value, std::true_type) {
    std::cout << "copy from lvalue: " << value << '\n';
}

template <typename T>
void store_impl(T&& value, std::false_type) {
    std::cout << "move from rvalue: " << value << '\n';
}

template <typename T>
void store(T&& value) {
    using tag = std::is_lvalue_reference<T>;
    store_impl(std::forward<T>(value), tag{});
}

int main() {
    std::string s = "hello";

    store(s);                    // lvalue \-> copy path
    store(std::string("temp"));  // rvalue \-> move path
}
```

#### How it works

When `store(s)` is called:

* `T` becomes `std::string&`
* `std::is_lvalue_reference<T>` is `std::true_type`
* the lvalue overload is selected

When `store(std::string("temp"))` is called:

* `T` becomes `std::string`
* `std::is_lvalue_reference<T>` is `std::false_type`
* the rvalue overload is selected

#### Rule of thumb

Use this when:

* the public interface should stay simple
* implementation choices depend on type category or traits
* `if constexpr` is not the clearest structure

In modern C++, `if constexpr` is often simpler, but tag dispatch remains a useful pattern for generic libraries.

### Best practices

* Prefer standard traits before creating custom ones.
* Keep policies narrow and well documented.
* Use tag dispatch when behavior really depends on a category, not on a runtime flag.

***

## 6. Tuples, Variants, `std::visit`, and `std::apply`

These utilities help express structured heterogeneous data.

### `std::tuple`

A fixed-size group of values with different types.

### `std::variant`

A type-safe union that holds exactly one alternative at a time.

### `std::visit`

Dispatches to the correct handler for the active variant alternative.

### `std::apply`

Expands a tuple into arguments for a callable.

### Example

More precisely, the operator being folded is the comma operator, so it means:

* evaluate the first print
* then the second
* then the third That is why the output appears in order. A useful way to recognize fold expressions:
* (args + ...) -> combine all arguments with +
* (... && args) -> combine all arguments with &&
* (expr, ...) -> do expr once for each argument So this particular one is best understood as a compact “do this for every unpacked tuple element” expression.

```cpp
std::tuple<int, double, std::string> t{42, 3.14, "hello"};
const auto& [id, score, name] = t;
std::cout << id << ' ' << score << ' ' << name << '\n';

std::apply([](const auto&... args) {
    ((std::cout << args << ' '), ...); // The expression inside uses a fold expression to stream each argument to std::cout, followed by a space.
}, t);

std::variant<int, std::string> v = std::string{"C++"};
std::visit([](const auto& value) {
    std::cout << value << '\n';
}, v);
```

### Best practices

* Use structured bindings to unpack tuples when names help readability.
* Prefer `std::variant` to raw unions in application code.
* Keep variant alternatives meaningful and limited in number.

***

## 7. pImpl (Pointer to Implementation)

The pImpl idiom hides implementation details behind an opaque pointer. It reduces header dependencies and helps keep rebuild times under control.

### Example

```cpp
// widget.h
#include <memory>

class Widget {
public:
    Widget();
    ~Widget();

    void draw();

private:
    struct Impl;
    std::unique_ptr<Impl> impl_;
};

// widget.cpp
struct Widget::Impl {
    std::string name;
    int id = 0;
};

Widget::Widget() : impl_(std::make_unique<Impl>()) {}
Widget::~Widget() = default;

void Widget::draw() {
    // use impl_
}
```

### Best practices

* Use `std::unique_ptr<Impl>` unless you genuinely need shared ownership.
* Define the destructor in the `.cpp` file, where `Impl` is complete.
* Use pImpl when hiding dependencies is worth the extra indirection.

### Trade-off

You gain build isolation, but you pay with one more allocation and one more pointer indirection.

***

## 8. Lambdas

Lambdas are anonymous function objects. They are one of the most useful features in modern C++ because they keep local logic local.

### Best practices

* Prefer explicit captures over `[=]` or `[&]` in non-trivial code.
* Use init-capture for move-only state.

  An init-capture lets a lambda create its own captured variable from an expression, which is useful for move-only types such as std::unique\_ptr.

  ```
  auto p = std::make_unique<int>(42);

  auto print = [ptr = std::move(p)]() {
      std::cout << *ptr << '\n';
  };

  print();
  // `p` is now empty after the move.
  ```

  Here, ptr = std::move(p) initializes the lambda’s captured state by moving ownership into the lambda. A plain capture like \[p] would try to copy p, which does not work for std::unique\_ptr.
* Mark the lambda `mutable` only when you intentionally want stateful behavior.

  `mutable` is needed when a lambda captures by value and must modify its own stored copy.

  1. Counter state:

  ```cpp
  auto counter = [i = 0]() mutable {
      return i++;
  };

  counter(); // 0
  counter(); // 1
  counter(); // 2
  ```

  Without `mutable`, `i++` is not allowed because the lambda call operator is `const` by default.

  2. Value capture that changes internally:

  ```cpp
  int start = 10;

  auto step = [x = start]() mutable {
      x += 5;
      return x;
  };

  step(); // 15
  step(); // 20
  start;  // still 10
  ```

  This changes only the lambda’s private copy, not the original variable.

  3. Move-only state that gets consumed:

  ```cpp
  #include <memory>

  auto reader = [p = std::make_unique<int>(42)]() mutable {
      int value = *p;
      p.reset();
      return value;
  };
  ```

  `mutable` is required because `p.reset()` modifies the captured `std::unique_ptr`.

  4. Case where `mutable` is usually not needed:

  ```cpp
  int x = 0;

  auto inc = [&x]() {
      ++x;
  };
  ```

  Here `x` is captured by reference, so the lambda is not modifying its own stored value.

  Rule of thumb: use `mutable` only when the lambda should behave like a small stateful object.
* Use generic lambdas when the callable should work across types. Generic lambdas use auto parameters so one callable can work with multiple types.

  ```
  auto add = [](auto a, auto b) {
      return a + b;
  };

  std::cout << add(1, 2) << '\n';
  std::cout << add(1.5, 2.5) << '\n';
  std::cout << add(std::string("ab"), std::string("cd")) << '\n';
  ```

### Example

```cpp
auto add = [](auto x, auto y) {
    return x + y;
};

auto counter = [i = 0]() mutable {
    return i++;
};

auto ptr_reader = [ptr = std::make_unique<int>(42)] {
    return *ptr;
};
```

### Mental model

A lambda is essentially a compiler-generated class with an `operator()`.

***

## 9. Custom Streaming Operators

Overloading `operator<<` makes user-defined types print naturally.

### Example

```cpp
struct Point {
    int x;
    int y;
};

std::ostream& operator<<(std::ostream& os, const Point& p) {
    return os << '(' << p.x << ", " << p.y << ')';
}
```

### Best practices

* Define the operator as a non-member in the same namespace as the type.
* Return `std::ostream&` so chained streaming works.
* Keep formatting unsurprising and side-effect free.

***

## 10. `constexpr`

`constexpr` means a value or function can participate in compile-time evaluation when given constant inputs.

### Related terms

* `constexpr`: may run at compile time
* `consteval`: must run at compile time
* `constinit`: static storage object must be initialized at compile time

### Example

```cpp
constexpr int factorial(int n) {
    int result = 1;
    for (int i = 2; i <= n; ++i) {
        result *= i;
    }
    return result;
}

static_assert(factorial(5) == 120);
```

```cpp
consteval int square(int x) {
    return x * x;
}

constexpr int a = square(5);   // OK

int main() {
    constexpr int b = square(6); // OK

    int n = 7;
    // int c = square(n);        // Error: `n` is not a compile-time constant
}
```

```cpp
constinit int global_value = 42;

constexpr int make_id() {
    return 7;
}

constinit int global_id = make_id();

int main() {
    return global_value + global_id;
}

// a failing example
int runtime_value() {
    return 42;
}

constinit int x = runtime_value();  // error because runtime_value() is not a constant expression
```

### Best practices

* Use `constexpr` for pure, deterministic helpers that naturally fit compile-time use.
* Prefer `constexpr` constants over macros.
* Do not force `constexpr` everywhere; use it where it improves API quality or correctness.

***

## 11. `auto`

`auto` delegates type deduction to the compiler. Used well, it makes code shorter and more accurate.

### Best practices

* Use `auto` when the type is obvious or noisy.
* Use `const auto&` in range loops to avoid unnecessary copies.
* Use `decltype(auto)` when you need exact return-type preservation, including references.
* Avoid `auto` when the deduced type would hide an important detail.

### Example

```cpp
std::vector<int> values{1, 2, 3};

for (const auto& value : values) {
    std::cout << value << '\n';
}

decltype(auto) get_global() {
    static int x = 42;
    return (x); // returns int&, without decltype(auto) this would return int by value
}
```

### Rule of thumb

Use `auto` to remove repetition, not to obscure meaning.

***

## 12. Lambdas vs Macro Hackery

Macros are pure text substitution. Lambdas are typed, scoped, and visible to the compiler. In modern C++, lambdas should usually win.

### Example

```cpp
#define TRACE(expr)                                                     \
    do {                                                                \
        auto&& value = (expr);                                          \
        std::cout << #expr << " = " << value                            \
                  << " (" << __FILE__ << ':' << __LINE__ << ")\n";      \
    } while (0)
```

### Best practices

* Prefer lambdas or inline functions over macros for callable logic.
* Use macros only where the preprocessor adds something unique, such as `__FILE__`, `__LINE__`, or conditional compilation.
* If a macro evaluates an expression, make sure it does so exactly once.

### Practical guidance

Macros are sometimes necessary. They should still be small, explicit, and hard to misuse.

***

## 13. Sequencing and Evaluation Order

This topic matters whenever one expression has multiple side effects. If you do not know the sequencing rules, you can write code that is confusing or undefined.

### Important points

* Function arguments are still evaluated in **unspecified order**.
* In `a = b`, the right-hand side is evaluated before the assignment happens.
* Chaining complex side effects in one expression is usually a readability and correctness smell.

### Bad example

```cpp
f(i++, i++);
```

This is valid in modern C++, but the order of argument evaluation is unspecified, so the result is still hard to reason about.

### Best practices

* Split side effects into separate statements.
* Do not rely on subtle sequencing rules for normal application code.
* Write expressions so the intended order is obvious without language-lawyer knowledge.

***

## 14. Aliasing and Strict Aliasing

Aliasing means two references or pointers refer to the same storage. The strict aliasing rule lets the optimizer assume that unrelated pointer types usually do not alias.

### Why it matters

Violating strict aliasing can produce undefined behavior, especially in optimized builds.

### Best practices

* Use `std::bit_cast` in C++20 for safe reinterpretation by value.
* Use `std::memcpy` when moving raw bytes between representations.
* Avoid reading an object through an unrelated pointer type created with `reinterpret_cast`.

### Example

```cpp
float f = 3.14f;
std::uint32_t bits = std::bit_cast<std::uint32_t>(f);
```

### Bad example

```cpp
int x = 42;
float* p = reinterpret_cast<float*>(&x); // undefined behavior if used for type punning
```

***

## 15. Global / Static Storage vs Stack vs Heap

This topic is really about **storage duration** and lifetime.

### Main categories

* **Static storage duration**: globals, namespace-scope objects, `static` locals
* **Automatic storage duration**: ordinary local variables, often called "stack objects"
* **Dynamic storage duration**: objects created with `new`, usually managed by smart pointers

### Best practices

* Prefer automatic storage when lifetime is simple.
* Prefer owning containers and smart pointers over raw heap management.
* Avoid non-trivial global state unless you have a clear initialization strategy.
* Be aware that "stack" and "heap" are useful mental models, but the standard speaks in terms of storage duration and lifetime.

### Practical warning

Global initialization order across translation units can be tricky. A function-local static is often safer than a namespace-scope mutable object.

***

## 16. Smart Pointers

Smart pointers encode ownership rules in types.

### `std::unique_ptr`

Use for exclusive ownership. This should be the default owning pointer.

### `std::shared_ptr`

Use when ownership is genuinely shared across independent lifetimes.

### `std::weak_ptr`

Use to observe a `shared_ptr`-managed object without extending its lifetime.

### Example

```cpp
auto p = std::make_unique<int>(42);

auto shared = std::make_shared<std::string>("hello");
std::weak_ptr<std::string> weak = shared;

if (auto locked = weak.lock()) {
    std::cout << *locked << '\n';
}
```

### Best practices

* Prefer `std::unique_ptr` by default.
* Use `std::make_unique` and `std::make_shared`.
* Avoid `shared_ptr` in hot paths unless shared ownership is truly part of the design.
* Use raw pointers and references for non-owning access, not for ownership.

***

## 17. Virtual Dispatch

Virtual functions provide runtime polymorphism. They are the right tool when behavior depends on a runtime-selected concrete type.

### Example

```cpp
#include <memory>

class Shape {
public:
    virtual ~Shape() = default;
    virtual void draw() const = 0;
};

class Circle : public Shape {
public:
    void draw() const override {
        std::cout << "Circle\n";
    }
};

std::unique_ptr<Shape> shape = std::make_unique<Circle>();
shape->draw(); // calls Circle::draw at runtime
```

### Best practices

* Mark overrides with `override`.
* Make the base destructor virtual in polymorphic hierarchies.
* Use `final` when preventing further overriding is intentional.
* Do not call virtual functions from constructors or destructors expecting derived behavior.

### Trade-off

Virtual dispatch adds indirection and usually prevents some compile-time optimizations, but it is often the cleanest modeling choice.

***

## 18. Translation Units, ODR, `extern`, `static`, `inline`, and Anonymous Namespaces

Understanding linkage rules avoids many subtle build and linker issues.

### Translation unit

A translation unit is a source file after preprocessing. Roughly speaking, each `.cpp` file plus everything it includes becomes one translation unit.

### ODR

The One Definition Rule says that entities requiring a single definition in the program must have exactly one, unless the language explicitly allows otherwise, such as for templates or inline functions.

### Linkage tools

* `extern`: refers to a definition elsewhere
* `static` at namespace scope: gives internal linkage
* `inline`: allows identical definitions across translation units
* anonymous namespace: another common way to give internal linkage

### Best practices

* Put declarations in headers and definitions in source files unless the entity must be inline.
* Use anonymous namespaces for translation-unit-local helpers in `.cpp` files.
* Avoid defining non-inline variables in headers.
* Use header guards or `#pragma once`.

***

## 19. Operator Overloading

Operator overloading can make user-defined types natural to use, but only when the semantics stay intuitive.

### Example

```cpp
class Fraction {
public:
    Fraction(int num, int den) : num_(num), den_(den) {}

    Fraction& operator+=(const Fraction& other) {
        num_ = num_ * other.den_ + other.num_ * den_;
        den_ *= other.den_;
        return *this;
    }

    friend Fraction operator+(Fraction lhs, const Fraction& rhs) {
        lhs += rhs;
        return lhs;
    }

    explicit operator double() const {
        return static_cast<double>(num_) / den_;
    }

private:
    int num_;
    int den_;
};
```

### Best practices

* Preserve the normal meaning users expect from the operator.
* Prefer non-member overloads for symmetric binary operators.
* Use `explicit` conversion operators unless implicit conversion is clearly safe and intended.
* Do not overload operators just because you can.

***

## 20. RAII (Resource Acquisition Is Initialization)

RAII is one of the central ideas in C++. A resource is acquired during object construction and released during destruction.

### Why it matters

RAII gives you deterministic cleanup, simpler control flow, and strong exception-safety foundations.

### Example

```cpp
void process_file(const std::string& path) {
    std::ifstream file(path);
    if (!file) {
        throw std::runtime_error("failed to open file");
    }

    std::string line;
    std::getline(file, line);
} // file closes automatically here
```

### Common RAII types

* `std::vector`
* `std::string`
* `std::unique_ptr`
* `std::lock_guard`
* `std::scoped_lock`
* `std::ifstream`

### Best practices

* Every owned resource should have a clear RAII owner.
* Destructors should not throw.
* Prefer standard RAII wrappers over hand-written cleanup logic.

***

## Final Takeaways

Modern C++ works best when you combine high-level design with low-level awareness:

* Use the type system to encode ownership, constraints, and intent.
* Prefer language features over macros and manual conventions.
* Keep expressions simple when evaluation order matters.
* Understand the low-level rules around lifetime, aliasing, and linkage.
* Reach for RAII first whenever a resource needs cleanup.

That combination is what makes modern C++ both expressive and fast.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://www.damonyuan.com/tech/260204-morden-c++-best-practice-and-low-level-operation-explained.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
