From 4d483b3edb4d5df45c3960a1423537cc909aff3e Mon Sep 17 00:00:00 2001 From: Cem Dervis Date: Sat, 31 Jan 2026 01:12:17 +0100 Subject: [PATCH 1/2] Add improved explanation files --- content/auto.md | 51 +++++++++ content/bit-cast.md | 39 +++++++ content/char8-t-compatibility.md | 27 +++++ content/char8-t.md | 40 +++++++ content/concepts.md | 54 ++++++++++ content/constexpr-exceptions.md | 45 ++++++++ content/constexpr-virtual-inheritance.md | 44 ++++++++ content/constexpr-virtual.md | 55 ++++++++++ content/constinit.md | 45 ++++++++ content/contracts.md | 38 +++++++ content/coroutines.md | 60 +++++++++++ content/debugging.md | 40 +++++++ content/deducing-this.md | 60 +++++++++++ content/delete-reason.md | 40 +++++++ content/designated-initializers.md | 49 +++++++++ content/embed.md | 34 ++++++ content/enumerate.md | 38 +++++++ content/execution.md | 35 ++++++ content/expected.md | 51 +++++++++ content/filesystem.md | 46 ++++++++ content/flat-map.md | 44 ++++++++ content/format.md | 42 ++++++++ content/generator.md | 52 +++++++++ content/generic-lambdas.md | 48 +++++++++ content/hardening.md | 43 ++++++++ content/hive.md | 41 +++++++ content/if-consteval.md | 46 ++++++++ content/if-constexpr.md | 55 ++++++++++ content/inplace-vector.md | 38 +++++++ content/jthread.md | 44 ++++++++ content/lambdas.md | 44 ++++++++ content/likely-unlikely.md | 52 +++++++++ content/make-unique.md | 40 +++++++ content/mdspan.md | 39 +++++++ content/modules.md | 43 ++++++++ content/multidim-subscript.md | 42 ++++++++ content/optional-ref.md | 51 +++++++++ content/optional.md | 51 +++++++++ content/p0330_literal_suffix_size_t.md | 16 --- content/p1967_embed.md | 24 ----- content/p2169_placeholder_with_no_name.md | 35 ------ content/p2300_execution.md | 4 - content/p2546_debugging.md | 20 ---- content/p2573.md | 32 ------ content/p2589_static_subscript_operator.md | 22 ---- content/p2741_static_assert_msgs.md | 50 --------- content/p2809_trivial_infinite_loops_ub.md | 27 ----- content/p2900.md | 37 ------- content/p2988_optional_ref.md | 43 -------- content/p2996_reflection.md | 102 ------------------ content/p3471_hardening.md | 64 ----------- content/p3533.md | 38 ------- content/p3697_minor_additions_to_hardening.md | 3 - content/pack-indexing.md | 37 +++++++ content/placeholder-variable.md | 34 ++++++ content/print.md | 52 +++++++++ content/println-blank-lines.md | 43 ++++++++ content/ranges-to.md | 47 ++++++++ content/ranges.md | 50 +++++++++ content/reflection.md | 85 +++++++++++++++ content/simd.md | 42 ++++++++ content/source-location.md | 41 +++++++ content/span.md | 48 +++++++++ content/stacktrace.md | 43 ++++++++ content/static-call-operator.md | 45 ++++++++ content/static-subscript-operator.md | 34 ++++++ content/structured-bindings.md | 61 +++++++++++ content/three-way-comparison.md | 53 +++++++++ content/trivial-infinite-loops.md | 44 ++++++++ 69 files changed, 2465 insertions(+), 517 deletions(-) create mode 100644 content/auto.md create mode 100644 content/bit-cast.md create mode 100644 content/char8-t-compatibility.md create mode 100644 content/char8-t.md create mode 100644 content/concepts.md create mode 100644 content/constexpr-exceptions.md create mode 100644 content/constexpr-virtual-inheritance.md create mode 100644 content/constexpr-virtual.md create mode 100644 content/constinit.md create mode 100644 content/contracts.md create mode 100644 content/coroutines.md create mode 100644 content/debugging.md create mode 100644 content/deducing-this.md create mode 100644 content/delete-reason.md create mode 100644 content/designated-initializers.md create mode 100644 content/embed.md create mode 100644 content/enumerate.md create mode 100644 content/execution.md create mode 100644 content/expected.md create mode 100644 content/filesystem.md create mode 100644 content/flat-map.md create mode 100644 content/format.md create mode 100644 content/generator.md create mode 100644 content/generic-lambdas.md create mode 100644 content/hardening.md create mode 100644 content/hive.md create mode 100644 content/if-consteval.md create mode 100644 content/if-constexpr.md create mode 100644 content/inplace-vector.md create mode 100644 content/jthread.md create mode 100644 content/lambdas.md create mode 100644 content/likely-unlikely.md create mode 100644 content/make-unique.md create mode 100644 content/mdspan.md create mode 100644 content/modules.md create mode 100644 content/multidim-subscript.md create mode 100644 content/optional-ref.md create mode 100644 content/optional.md delete mode 100644 content/p0330_literal_suffix_size_t.md delete mode 100644 content/p1967_embed.md delete mode 100644 content/p2169_placeholder_with_no_name.md delete mode 100644 content/p2300_execution.md delete mode 100644 content/p2546_debugging.md delete mode 100644 content/p2573.md delete mode 100644 content/p2589_static_subscript_operator.md delete mode 100644 content/p2741_static_assert_msgs.md delete mode 100644 content/p2809_trivial_infinite_loops_ub.md delete mode 100644 content/p2900.md delete mode 100644 content/p2988_optional_ref.md delete mode 100644 content/p2996_reflection.md delete mode 100644 content/p3471_hardening.md delete mode 100644 content/p3533.md delete mode 100644 content/p3697_minor_additions_to_hardening.md create mode 100644 content/pack-indexing.md create mode 100644 content/placeholder-variable.md create mode 100644 content/print.md create mode 100644 content/println-blank-lines.md create mode 100644 content/ranges-to.md create mode 100644 content/ranges.md create mode 100644 content/reflection.md create mode 100644 content/simd.md create mode 100644 content/source-location.md create mode 100644 content/span.md create mode 100644 content/stacktrace.md create mode 100644 content/static-call-operator.md create mode 100644 content/static-subscript-operator.md create mode 100644 content/structured-bindings.md create mode 100644 content/three-way-comparison.md create mode 100644 content/trivial-infinite-loops.md diff --git a/content/auto.md b/content/auto.md new file mode 100644 index 0000000..518cee3 --- /dev/null +++ b/content/auto.md @@ -0,0 +1,51 @@ +--- +execute: true +--- + +## What It Does + +The `auto` keyword specifies that a variable's type is deduced from its initializer. +The compiler performs type deduction at the point of declaration, making explicit type names unnecessary. + +## Why It Matters + +Certain types are verbose or unnameable (e.g. lambda closure types and nested container iterators). +Automatic type deduction eliminates redundant type declarations, prevents implicit conversion errors, and reduces +maintenance overhead when initializer types change. + +## Example + +```cpp +#include +#include +#include + +using namespace std; + +int main() { + // Instead of: vector::iterator it = vec.begin(); + auto vec = vector{1, 2, 3, 4, 5}; + auto it = vec.begin(); + + // Complex types become manageable + auto data = map>{ + {"scores", {1, 2, 3}}, + }; + + for (auto& [key, values] : data) { + println("{}: {}", key, values); + } + + // Required for lambdas (their type is unnameable) + auto greet = [](string_view name) { + println("Hello, {}!", name); + }; + + greet("world"); + + // Function return type deduction + auto multiply = [](auto a, auto b) { return a * b; }; + + println("Product: {}", multiply(4, 2)); +} +``` diff --git a/content/bit-cast.md b/content/bit-cast.md new file mode 100644 index 0000000..8b4006a --- /dev/null +++ b/content/bit-cast.md @@ -0,0 +1,39 @@ +--- +execute: true +--- + +## What It Does + +`std::bit_cast()` reinterprets the object representation of one type as another type. +Both source and destination types must be trivially copyable and have the same size. +The operation is well-defined and permitted in constant expressions. + +## Why It Matters + +Type punning is accessing an object's raw bytes as if they were a different type. +Traditional type punning via unions or pointer casts invokes undefined behavior. +`std::bit_cast()` provides a portable, well-defined mechanism for bit reinterpretation +that is valid in `constexpr` contexts. + +## Example + +```cpp +#include +#include +#include + +int main() { + // View float's bits as integer + auto f = 1.5f; + auto bits = std::bit_cast(f); + std::println("1.5f as bits: {:#010x}", bits); + + // Convert back + auto f2 = std::bit_cast(bits); + std::println("1.5f restored from bits: {}", f2); + + // Works at compile time + constexpr auto pi_bits = std::bit_cast(3.14159265358979); + static_assert(pi_bits == 4614256656552045841); +} +``` diff --git a/content/char8-t-compatibility.md b/content/char8-t-compatibility.md new file mode 100644 index 0000000..d8f1457 --- /dev/null +++ b/content/char8-t-compatibility.md @@ -0,0 +1,27 @@ +## What It Does + +This allows UTF-8 string literals (`u8"..."`) to initialize arrays of `char`, `unsigned char`, and `signed char`. +This restores initialization patterns that were valid before C++20 introduced +`char8_t`. The fix applies only to array initialization, **not** pointer conversions. + +## Why It Matters + +C++20 changed `u8"..."` literals from `const char[]` to `const char8_t[]`, breaking existing code +that initialized character arrays from UTF-8 literals. Code such as `const char s[] = u8"text";` +became ill-formed, requiring workarounds like compiler flags or shim layers. This proposal +restores the array initialization while preserving the type distinction in other contexts. + +## Example + +```cpp +#include + +int main() { + // Array initialization from u8 literals (restored by P2513) + const char utf8[] = u8"Hello, 世界"; + const unsigned char raw[] = u8"\xC2\xA9"; // copyright symbol + + std::println("UTF-8 string: {}", utf8); + std::println("Raw bytes: {} {}", raw[0], raw[1]); // 194 169 +} +``` diff --git a/content/char8-t.md b/content/char8-t.md new file mode 100644 index 0000000..dc3aba7 --- /dev/null +++ b/content/char8-t.md @@ -0,0 +1,40 @@ +--- +execute: true +--- + +## What It Does + +`char8_t` is a distinct fundamental type representing UTF-8 encoded character data. +String literals with the `u8` prefix have type `const char8_t[]` rather than `const char[]`, +establishing UTF-8 as a distinct type in the type system. + +## Why It Matters + +The `char` type conflates encoding-agnostic bytes with text data. +`char8_t` encodes UTF-8 explicitly in the type system, enabling APIs to enforce +UTF-8 input requirements and permitting the compiler to detect encoding mismatches at compile time. + +## Example + +```cpp +#include +#include + +void process_utf8(std::u8string_view text) { + std::println("Length in code units: {}", text.size()); +} + +int main() { + // UTF-8 literals have type char8_t + auto greeting = u8"Hello, 世界!"; + + process_utf8(greeting); + process_utf8(u8"UTF-8 string"); + + // Can't mix with char accidentally + // const char* p = u8"text"; // Error! + + // Convert when needed + auto as_chars = reinterpret_cast(greeting); +} +``` diff --git a/content/concepts.md b/content/concepts.md new file mode 100644 index 0000000..dbfe90f --- /dev/null +++ b/content/concepts.md @@ -0,0 +1,54 @@ +--- +execute: true +--- + +## What It Does + +Concepts are named predicates that constrain template parameters. +Constraints are expressed as requirements on type properties, replacing SFINAE-based +constraint checking. The compiler validates these constraints at the point of instantiation +and produces diagnostic messages when constraints are violated. + +Standard concepts are provided in ``; user-defined concepts are specified +using `requires` expressions. + +## Why It Matters + +Template instantiation errors without concepts manifest as verbose, deeply nested +template backtraces through library implementation details. +Concepts shift constraint checking to the call site with direct diagnostic messages +(e.g., "type X does not satisfy Sortable"). +Additionally, concepts enable function overloading and partial ordering based on type properties. + +## Example + +```cpp +#include +#include + +// Using standard concepts +template +auto double_it(T value) { + return value * 2; +} + +// Custom concept +template +concept Printable = requires(T t) { + // States that the following expression must be valid for T: + std::println("{}", t); +}; + +template +void log(const T& value) { + std::println("[LOG] {}", value); +} + +int main() { + double_it(42); // OK + // double_it(3.14); // Error: double doesn't satisfy std::integral + + log("hello"); // OK + log(123); // OK +} +``` diff --git a/content/constexpr-exceptions.md b/content/constexpr-exceptions.md new file mode 100644 index 0000000..887bbca --- /dev/null +++ b/content/constexpr-exceptions.md @@ -0,0 +1,45 @@ +--- +execute: false +--- + +## What It Does + +With this, exceptions may be thrown and caught during constant expression evaluation. +If an exception is not caught within the constant expression context, the result +is a compile-time error. If caught within the evaluation context, evaluation continues. + +## Why It Matters + +`constexpr` functions previously required distinct error-handling mechanisms for +compile-time versus runtime evaluation contexts. +Exception support in constant expressions unifies error handling, permitting identical +code to operate in both compile-time and runtime contexts. + +## Example + +```cpp +#include + +constexpr int safe_divide(int a, int b) { + if (b == 0) { + throw std::invalid_argument("division by zero"); + } + + return a / b; +} + +constexpr int safe_compute(int x, int y) { + try { + return safe_divide(x, y); + } catch (const std::invalid_argument&) { + return 0; // fallback for division by zero + } +} + +// Works at compile time +static_assert(safe_compute(10, 2) == 5); +static_assert(safe_compute(10, 0) == 0); // caught and handled + +// This would fail to compile (uncaught exception): +// constexpr int bad = safe_divide(1, 0); +``` diff --git a/content/constexpr-virtual-inheritance.md b/content/constexpr-virtual-inheritance.md new file mode 100644 index 0000000..f067cd1 --- /dev/null +++ b/content/constexpr-virtual-inheritance.md @@ -0,0 +1,44 @@ +## What It Does + +This proposal removes the restriction that prevented `constexpr` member functions in classes +with virtual base classes. Previously, constructors, destructors, and other member functions +in virtually-inheriting classes could not be declared `constexpr`. Now all such functions +are eligible for constant evaluation. + +## Why It Matters + +For example, virtual inheritance is used in the standard library's iostream hierarchy. The restriction +blocked `constexpr std::stringstream` and compile-time stream-based parsing. Removing this +limitation enables constant evaluation for any class hierarchy, regardless of whether it +uses virtual inheritance. + +## Example + +```cpp +#include + +struct Base { + int value; + constexpr Base(int v) : value(v) {} + constexpr virtual int get() const { return value; } +}; + +struct Left : virtual Base { + constexpr Left(int v) : Base(v) {} +}; + +struct Right : virtual Base { + constexpr Right(int v) : Base(v) {} +}; + +struct Diamond : Left, Right { + constexpr Diamond(int v) : Base(v), Left(v), Right(v) {} + constexpr int get() const override { return value * 2; } +}; + +int main() { + constexpr auto d = Diamond(21); + static_assert(d.get() == 42); + std::println("Diamond value: {}", d.get()); +} +``` diff --git a/content/constexpr-virtual.md b/content/constexpr-virtual.md new file mode 100644 index 0000000..3424f57 --- /dev/null +++ b/content/constexpr-virtual.md @@ -0,0 +1,55 @@ +--- +execute: false +--- + +## What It Does + +With this, virtual function calls are permitted within constant expressions. +When the dynamic type is known at compile time, the compiler resolves the virtual +dispatch and evaluates the call during constant expression evaluation. + +## Why It Matters + +`constexpr` evaluation and virtual dispatch were previously mutually exclusive. +`constexpr` virtual functions enable inheritance hierarchies and polymorphic dispatch +in constant expressions, supporting polymorphic algorithms and data structures at compile time. + +## Example + +```cpp +struct Shape { + constexpr virtual double area() const = 0; + constexpr virtual ~Shape() = default; +}; + +struct Circle : Shape { + double radius; + + constexpr Circle(double r) + : radius(r) + {} + + constexpr double area() const override { + return 3.14159 * radius * radius; + } +}; + +struct Rectangle : Shape { + double width, height; + + constexpr Rectangle(double w, double h) + : width(w), height(h) + {} + + constexpr double area() const override { + return width * height; + } +}; + +constexpr double get_area(const Shape& s) { + return s.area(); // virtual call at compile time +} + +static_assert(get_area(Circle(5.0)) > 78.0); +static_assert(get_area(Rectangle(3.0, 4.0)) == 12.0); +``` diff --git a/content/constinit.md b/content/constinit.md new file mode 100644 index 0000000..b146176 --- /dev/null +++ b/content/constinit.md @@ -0,0 +1,45 @@ +--- +execute: true +--- + +## What It Does + +`constinit` specifies that a variable with static or thread-local storage duration +is initialized by constant initialization. Unlike `constexpr`, the variable is **not** +`const`-qualified and may be modified at runtime; only the initialization is guaranteed +to occur at compile time. + +## Why It Matters + +The static initialization order fiasco occurs when global variables with dynamic +initializers have dependencies on each other. +`constinit` ensures constant initialization precedes dynamic initialization, +eliminating undefined behavior from initialization order dependencies. + +## Example + +```cpp +#include +#include + +// Guaranteed compile-time initialization +constinit auto global_count = 42; +constinit auto some_array = std::array{1, 2, 3}; + +// NOT constexpr - can be modified at runtime +void increment() { + ++global_count; +} + +// Error: not a constant expression +// constinit int x = some_runtime_function(); + +// Thread-local works too +constinit thread_local int per_thread_id = 0; + +int main() { + increment(); // global_count is now 43 + std::println("global_count: {}", global_count); + std::println("some_array: {}", some_array); +} +``` diff --git a/content/contracts.md b/content/contracts.md new file mode 100644 index 0000000..2656d28 --- /dev/null +++ b/content/contracts.md @@ -0,0 +1,38 @@ +--- +execute: false +--- + +## What It Does + +Contracts specify function preconditions (requirements on input), postconditions +(guarantees on output), and assertions (invariants at specific program points). +These conditions are declared using `pre`, `post`, and `contract_assert` in function +signatures and bodies. Violations are detected at runtime. + +## Why It Matters + +Invalid inputs or incorrect outputs may propagate far from their origin before +causing observable failure. +Contracts detect violations at the contract boundary and serve as executable +specifications of function requirements. + +## Example + +```cpp +#include + +double square_root(double x) + pre (x >= 0) // caller must provide non-negative input + post (r: r >= 0) // result will be non-negative +{ + return std::sqrt(x); +} + +void process_buffer(int* data, int size) + pre (data != nullptr) + pre (size > 0) +{ + contract_assert(size <= 1000); // internal sanity check + // ... process data +} +``` diff --git a/content/coroutines.md b/content/coroutines.md new file mode 100644 index 0000000..f511f64 --- /dev/null +++ b/content/coroutines.md @@ -0,0 +1,60 @@ +--- +execute: true +--- + +## What It Does + +Coroutines are functions that can suspend execution and resume at a later point. +Suspension points are indicated by `co_await` (awaiting a value), `co_yield` (producing +a value to the caller), and `co_return` (completing execution). +The compiler transforms coroutines into state machine implementations. + +## Why It Matters + +Asynchronous programming without coroutines requires callback nesting or explicit +state machine management. +Coroutines enable sequential structuring of asynchronous control flow, +making asynchronous logic explicit and composable. + +## Example + +```cpp +#include +#include + +struct Task { + struct promise_type { + auto get_return_object() { return Task(); } + auto initial_suspend() { return std::suspend_never(); } + auto final_suspend() noexcept { return std::suspend_never(); } + void return_void() {} + void unhandled_exception() {} + }; +}; + +struct Awaiter { + auto await_ready() { + return false; + } + + auto await_suspend(std::coroutine_handle<> h) { + std::println("Suspended..."); + return h; + } + + auto await_resume() { + std::println("Resumed!"); + return 42; + } +}; + +Task example() { + std::println("Starting"); + auto result = co_await Awaiter(); + std::println("Got: {}", result); +} + +int main() { + example(); +} +``` diff --git a/content/debugging.md b/content/debugging.md new file mode 100644 index 0000000..44edc60 --- /dev/null +++ b/content/debugging.md @@ -0,0 +1,40 @@ +--- +executable: false +--- + +## What It Does + +The `` header provides portable utilities for debugger interaction. +`std::breakpoint()` triggers a debugger breakpoint; `std::is_debugger_present()` +queries whether a debugger is attached to the process. + +## Why It Matters + +Prior to this facility, triggering a breakpoint programmatically required +platform-specific intrinsics (e.g., `__debugbreak()`, `__builtin_trap()`, `raise(SIGTRAP)`). +The standard debugging utilities provide a portable interface with defined behavior +when no debugger is attached. + +## Example + +```cpp +#include +#include + +void validate(int value) { + if (value < 0) { + std::println("Invalid value: {}", value); + + if (std::is_debugger_present()) { + std::breakpoint(); // pause here for inspection + } + } +} + +int main() { + // Unconditional breakpoint - useful for "stop here" debugging + std::breakpoint_if_debugging(); + + validate(-5); +} +``` diff --git a/content/deducing-this.md b/content/deducing-this.md new file mode 100644 index 0000000..f349975 --- /dev/null +++ b/content/deducing-this.md @@ -0,0 +1,60 @@ +## What It Does + +Member functions may declare an explicit `this` parameter that deduces the type and value category of the object expression. +A single function template with an explicit `this` parameter replaces separate `const` and non-`const` overloads. + +The syntax declares `this auto&& self` as the first parameter, making the implicit `this` pointer explicit and enabling type deduction. + +## Why It Matters + +Separate `const` and non-`const` overloads require duplicate function bodies or [CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) patterns with additional template infrastructure. +An explicit `this` parameter removes this duplication and enables recursive lambdas and CRTP-based designs without auxiliary templates. + +## Example + +```cpp +#include +#include +#include + +struct Widget { + std::string name; + + // One function handles both const and non-const + auto&& get_name(this auto&& self) { + return std::forward_like(self.name); + } + + // Recursive lambda now works naturally + void traverse(this auto const& self, int indentation, auto&& fn) { + fn(self, indentation); + + for (auto& child : self.children) { + child.traverse(indentation + 1, fn); + } + } + + std::vector children; +}; + +int main() { + auto widget = Widget { + .name = "SomeWidget", + .children = { + Widget { .name = "SomeChild1" }, + Widget { + .name = "SomeChild2", + .children = { + Widget { .name = "SomeChildChild" }, + }, + }, + }, + }; + + std::println("Widget name: {}", widget.get_name()); + + widget.traverse(0, [](const Widget& w, int indentation) { + std::println("{}{}", std::string(indentation, ' '), w.get_name()); + }); +} +``` diff --git a/content/delete-reason.md b/content/delete-reason.md new file mode 100644 index 0000000..22467be --- /dev/null +++ b/content/delete-reason.md @@ -0,0 +1,40 @@ +--- +execute: true +--- + +## What It Does + +Deleted functions may specify a message string as an argument to the `delete` specifier. +When a deleted function is selected by overload resolution, the compiler emits the specified message in the diagnostic. + +## Why It Matters + +Function deletion prevents unintended operations, but standard error messages provide no context regarding the rationale. +A user-specified message conveys the reason for deletion and may suggest alternative operations. + +## Example + +```cpp +class NonCopyable { +public: + NonCopyable() = default; + + NonCopyable(const NonCopyable&) + = delete("copying is expensive; use clone() for explicit copies"); + + NonCopyable& operator=(const NonCopyable&) + = delete("copying is expensive; use clone() for explicit copies"); + + NonCopyable clone() const { return NonCopyable(*this, 0); } + +private: + NonCopyable(const NonCopyable&, int) {} // internal copy +}; + +int main() { + auto a = NonCopyable(); + + // Error: can't copy NonCopyables via copy ctor or assignment: + auto b = a; +} +``` diff --git a/content/designated-initializers.md b/content/designated-initializers.md new file mode 100644 index 0000000..774217d --- /dev/null +++ b/content/designated-initializers.md @@ -0,0 +1,49 @@ +--- +execute: true +--- + +## What It Does + +Designated initializers initialize aggregate members by name using `.member = value` syntax. +Members may be omitted from the initializer list and are default-initialized; the syntax explicitly associates each initializer with its corresponding member. + +## Why It Matters + +Positional initialization is sensitive to member order; adding or reordering members changes the mapping of initializers without diagnostic. +Designated initializers are insensitive to member reordering and document the mapping between initializers and members explicitly. +The syntax is compatible with C99 designated initializers, with the additional constraint that designators must appear in declaration order in C++. + +## Example + +```cpp +#include +#include + +struct Config { + std::string host = "localhost"; + int port = 8080; + bool ssl = false; + int timeout_ms = 5000; +}; + +struct Point { int x, y, z; }; + +int main() { + // Clear what each value means + auto cfg = Config{ + .host = "example.com", + .port = 443, + .ssl = true + // timeout_ms uses default (0, because of int) + }; + + // More explicit than: + // auto cfg = Config{"example.com", 443, true, 5000}; + + std::println("cfg: {}; {}; {}; {}", cfg.host, cfg.port, cfg.ssl, cfg.timeout_ms); + + auto p = Point{.x = 1, .z = 3}; // y is default-initialized + + std::println("p: {}; {}; {}", p.x, p.y, p.z); +} +``` diff --git a/content/embed.md b/content/embed.md new file mode 100644 index 0000000..b1b0f29 --- /dev/null +++ b/content/embed.md @@ -0,0 +1,34 @@ +--- +show_only: true +--- + +## What It Does + +`#embed` is a preprocessor directive that embeds the contents of a binary file as a comma-separated list of integer literals. +The directive specifies a file path; an optional `limit(N)` parameter restricts the number of bytes embedded. + +## Why It Matters + +Binary resource embedding previously required external tools to convert files to C array syntax, or runtime file I/O. +`#embed` integrates resource embedding into the translation process, ensuring embedded data is synchronized with source files at compile time without, external tooling. + +## Example + +```cpp +#include +#include + +// Embed an entire file +const std::uint8_t icon[] = { + #embed "icon.png" +}; + +// Embed with a size limit +const std::uint8_t header[] = { + #embed "large_file.bin" limit(256) +}; + +std::span get_icon() { + return icon; +} +``` diff --git a/content/enumerate.md b/content/enumerate.md new file mode 100644 index 0000000..66fa921 --- /dev/null +++ b/content/enumerate.md @@ -0,0 +1,38 @@ +--- +execute: true +--- + +## What It Does + +`views::enumerate()` produces a range of tuples where each tuple contains an index and the corresponding element from the source range. +The view generates indices starting from zero and pairs them with elements from the adapted range. + +## Why It Matters + +Indexed iteration previously required explicit counter variables or calls to `std::distance()`. +`views::enumerate()` automates index generation and is compatible with any range, including those without random access or a known size. + +## Example + +```cpp +#include +#include +#include +#include + +using namespace std::string_literals; +using namespace std::string_view_literals; + +int main() { + auto names = std::vector{"Alice"s, "Bob"s, "Charlie"s}; + + for (const auto& [index, name] : std::views::enumerate(names)) { + std::println("{}: {}", index, name); + } + + // Works with any range + for (const auto& [i, c] : std::views::enumerate("Hello"sv)) { + std::println("Character {} is '{}'", i, c); + } +} +``` diff --git a/content/execution.md b/content/execution.md new file mode 100644 index 0000000..c00b7fa --- /dev/null +++ b/content/execution.md @@ -0,0 +1,35 @@ +--- +execute: true +--- + +## What It Does + +`std::execution` defines a framework for asynchronous and parallel computation based on three core abstractions: senders (represent deferred work), receivers (consume results), and schedulers (control execution context). + +Operations are composed by connecting senders through pipe operators, constructing a computation graph that remains deferred until connected to a receiver and started. + +## Why It Matters + +Asynchronous programming involves resource lifetime management, error propagation paths, and cancellation semantics. +`std::execution` specifies ownership semantics, defines cancellation behavior, and enables operation composition within a unified conceptual framework. + +## Example + +```cpp +#include +#include + +using namespace std::execution; + +int main() { + auto work = just(42) // start with a value + | then([](int x) { return x * 2; }) // transform it + | then([](int x) { + std::println("Result: {}", x); + return x; + }); + + // Execute synchronously and get the result + auto [result] = std::this_thread::sync_wait(work).value(); +} +``` diff --git a/content/expected.md b/content/expected.md new file mode 100644 index 0000000..54b05da --- /dev/null +++ b/content/expected.md @@ -0,0 +1,51 @@ +--- +execute: true +--- + +## What It Does + +`std::expected` is a class template that stores either a value of type `T` or an error of type `E`. +The type provides monadic operations (`and_then()`, `or_else()`, `transform()`) for composing computations that may fail. + +## Why It Matters + +Exceptions propagate outside the standard function return mechanism; error codes require explicit checking at each call site. +`std::expected` encodes error conditions in the return type and supports composition of error-handling operations through monadic member functions. + +## Example + +```cpp +#include +#include +#include +#include + +enum class ParseError { empty, invalid, overflow }; + +std::expected parse_int(std::string_view s) { + if (s.empty()) { + return std::unexpected(ParseError::empty); + } + + auto value = 0; + auto [ptr, ec] = std::from_chars(s.begin(), s.end(), value); + + if (ec == std::errc::result_out_of_range) { + return std::unexpected(ParseError::overflow); + } + + if (ec != std::errc() || ptr != s.end()) { + return std::unexpected(ParseError::invalid); + } + + return value; +} + +int main() { + const auto result = parse_int("42") + .transform([](int n) { return n * 2; }) + .value_or(-1); + + std::println("Result: {}", result); +} +``` diff --git a/content/filesystem.md b/content/filesystem.md new file mode 100644 index 0000000..cadd2b0 --- /dev/null +++ b/content/filesystem.md @@ -0,0 +1,46 @@ +--- +execute: false +--- + +## What It Does + +`std::filesystem` provides facilities for path manipulation, file status queries, directory iteration, and file system modification operations. +The library abstracts platform-specific file system interfaces into a portable API. + +## Why It Matters + +File system operations previously required platform-specific APIs or third-party libraries such as [Boost](https://www.boost.org/). +`std::filesystem` standardizes file system access, derived from Boost.Filesystem, eliminating external dependencies for portable file manipulation. + +## Example + +```cpp +#include +#include + +// Shortcut so that we don't have to always type std::filesystem +namespace fs = std::filesystem; + +int main() { + auto p = fs::path("/home/user/documents/report.txt"); + + std::println("Filename: {}", p.filename().string()); // report.txt + std::println("Extension: {}", p.extension().string()); // .txt + std::println("Parent: {}", p.parent_path().string()); // /home/user/documents + + // Check existence and properties + if (fs::exists(p)) { + std::println("Size: {} bytes", fs::file_size(p)); + } + + // Iterate directory + for (const auto& entry : fs::directory_iterator(".")) { + std::println("{}", entry.path().filename().string()); + } + + // Common operations + fs::create_directories("a/b/c"); + fs::copy("source.txt", "dest.txt"); + fs::remove("temp.txt"); +} +``` diff --git a/content/flat-map.md b/content/flat-map.md new file mode 100644 index 0000000..74c9ba0 --- /dev/null +++ b/content/flat-map.md @@ -0,0 +1,44 @@ +--- +execute: true +--- + +## What It Does + +`std::flat_map` and `std::flat_set` are associative containers that store elements in contiguous storage (typically `std::vector`) rather than tree nodes. +The containers maintain elements in sorted order and expose an interface compatible with `std::map` and `std::set`. + +## Why It Matters + +Tree-based containers allocate nodes individually, resulting in non-contiguous storage and decreased cache locality during traversal. +`std::flat_map` and `std::flat_set` store elements contiguously, improving cache performance for lookup operations at the cost of O(n) insertion complexity. + +## Example + +```cpp +#include +#include +#include + +int main() { + auto scores = std::flat_map(); + + scores["Alice"] = 95; + scores["Bob"] = 87; + scores["Charlie"] = 92; + + // Same interface as std::map + for (const auto& [name, score] : scores) { + std::println("{}: {}", name, score); + } + + // Contains: 1, 3, 4, 5 (sorted, unique) + auto numbers = std::flat_set{3, 1, 4, 1, 5}; + + std::println("numbers: {}", numbers); + + // Efficient lookup + if (numbers.contains(4)) { + std::println("Found 4"); + } +} +``` diff --git a/content/format.md b/content/format.md new file mode 100644 index 0000000..a00fcb2 --- /dev/null +++ b/content/format.md @@ -0,0 +1,42 @@ +--- +execute: true +--- + +## What It Does + +`std::format()` performs type-safe string formatting using replacement fields with format specifications. +Format specifiers control field width, alignment, precision, and numeric base. +Argument types are verified at compile time against the format string. + +## Why It Matters + +`printf()` format specifiers are not type-checked, resulting in undefined behavior when specifiers mismatch argument types. +`std::stringstream` requires explicit stream insertion operations and state management. +`std::format()` combines type safety with a declarative format string syntax. + +## Example + +```cpp +#include +#include +#include + +int main() { + // Basic formatting + auto s = std::format("Hello, {}!", "world"); + + // Positional arguments + auto msg = std::format("{1} before {0}", "second", "first"); + + // Format specifiers + std::println("{:>10}", "right"); // " right" + std::println("{:<10}", "left"); // "left " + std::println("{:^10}", "center"); // " center " + + // Numbers + std::println("{:08}", 42); // "00000042" + std::println("{:.2f}", 3.14159); // "3.14" + std::println("{:#x}", 255); // "0xff" + std::println("{:#b}", 7); // "0b111" +} +``` diff --git a/content/generator.md b/content/generator.md new file mode 100644 index 0000000..f334e26 --- /dev/null +++ b/content/generator.md @@ -0,0 +1,52 @@ +--- +execute: true +--- + +## What It Does + +`std::generator` is a coroutine type for lazy sequence generation. +Each `co_yield` expression suspends the coroutine, resuming when the next element is requested. +Elements are produced incrementally during iteration. + +## Why It Matters + +Container allocation is avoided for sequential data access. +Memory consumption is bounded by a single element, the type is compatible with range adaptors, and unbounded sequences can be expressed without resource exhaustion. + +## Example + +```cpp +#include +#include + +std::generator fibonacci(int limit) { + auto a = 0; + auto b = 1; + + while (a < limit) { + co_yield a; + auto next = a + b; + a = b; + b = next; + } +} + +std::generator iota(int start = 0) { + while (true) { + co_yield start++; + } +} + +int main() { + std::println("fib:"); + for (auto n : fibonacci(100)) { + std::println("{}", n); + } + + // Take first 5 from infinite sequence + std::println("first 5, starting with 10:"); + for (auto n : iota(10) | std::views::take(5)) { + std::println("{}", n); + } +} +``` diff --git a/content/generic-lambdas.md b/content/generic-lambdas.md new file mode 100644 index 0000000..b73cfed --- /dev/null +++ b/content/generic-lambdas.md @@ -0,0 +1,48 @@ +--- +execute: true +--- + +## What It Does + +Generic lambdas declare parameters with `auto`, producing a function call operator template. The compiler instantiates the appropriate specialization based on the argument types at each call site, so you don't need explicit template syntax in the lambda definition. + +## Why It Matters + +Prior to C++14, lambdas required explicit parameter types. Implementing type-generic behavior necessitated separate template functions. Generic lambdas integrate template instantiation directly into lambda expressions, enabling polymorphic inline function objects without separate template definitions. + +## Example + +```cpp +#include +#include +#include +#include + +using namespace std::string_literals; + +int main() { + // Works with any type + auto print = [](const auto& x) { + std::println("{}", x); + }; + + print(42); // int + print(3.14); // double + print("hello"); // const char* + + // Useful in algorithms + auto twice = [](auto x) { return x + x; }; + + std::println("{}", twice(5)); // 10 + std::println("{}", twice(2.5)); // 5.0 + std::println("{}", twice("ab"s)); // "abab" + + // Multiple auto parameters + auto add = [](auto a, auto b) { return a + b; }; + + auto nums = std::vector{3, 1, 4, 1, 5}; + std::sort(nums.begin(), nums.end(), [](auto a, auto b) { return a > b; }); + + std::println("nums: {:n}", nums); +} +``` diff --git a/content/hardening.md b/content/hardening.md new file mode 100644 index 0000000..156a174 --- /dev/null +++ b/content/hardening.md @@ -0,0 +1,43 @@ +--- +execute: true +flags: "-D_FORTIFY_SOURCE=3 -D_GLIBCXX_ASSERTIONS" +--- + +## What It Does + +Standard library hardening introduces runtime validation for operations with invalid preconditions. This includes bounds checking for container element access (`std::vector`, `std::span`, `std::string_view`) and state validation for types with invalid access conditions (`std::optional`, `std::expected`). When a contract violation is detected, the program terminates deterministically instead of invoking undefined behavior. + +The implementation utilizes C++26 contracts internally while remaining applicable to code without explicit contract annotations. + +## Why It Matters + +Out-of-bounds access and invalid state access constitute significant categories of security vulnerabilities and runtime failures. Hardening detects these contract violations at the point of occurrence, yielding deterministic program termination in place of undefined behavior. + +## Example + +```cpp +#include +#include +#include + +void process(std::span data) { + // With hardening enabled, this terminates immediately + // instead of reading garbage or crashing later, + // if data has fewer than 101 elements. + int value = data[100]; +} + +int main() { + auto v = std::vector{1, 2, 3}; + + // These would terminate with hardening: + // v[10]; // out of bounds + // v.front(); // if v were empty + // *v.begin() + 10; // iterator past end + + process(v); + + auto opt = std::optional(); + // *opt; // hardening: accessing empty optional +} +``` diff --git a/content/hive.md b/content/hive.md new file mode 100644 index 0000000..a88a91f --- /dev/null +++ b/content/hive.md @@ -0,0 +1,41 @@ +--- +execute: true +--- + +## What It Does + +`std::hive` is a container designed for high-frequency insertion and erasure operations. +Element erasure does not invalidate iterators or relocate other elements, providing iterator stability comparable to node-based containers. Elements are stored in contiguous blocks, maintaining cache locality absent in linked structures. + +The implementation maintains a free list of erased slots for reuse during subsequent insertions, combining iterator stability with block-contiguous storage. + +## Why It Matters + +Game engines, simulations, and entity-component systems frequently perform object insertion and removal. `std::vector` necessitates element relocation upon erasure; `std::list` exhibits suboptimal cache performance. `std::hive` provides iterator stability combined with block-contiguous element storage. + +## Example + +```cpp +#include +#include + +struct Entity { + int id; + float x; + float y; +}; + +int main() { + auto entities = std::hive(); + + auto it1 = entities.insert({1, 0.0f, 0.0f}); + auto it2 = entities.insert({2, 1.0f, 1.0f}); + auto it3 = entities.insert({3, 2.0f, 2.0f}); + + entities.erase(it2); // it1 and it3 remain valid + + for (const auto& e : entities) { + std::println("Entity {}: ({}, {})", e.id, e.x, e.y); + } +} +``` diff --git a/content/if-consteval.md b/content/if-consteval.md new file mode 100644 index 0000000..1977256 --- /dev/null +++ b/content/if-consteval.md @@ -0,0 +1,46 @@ +--- +execute: true +--- + +## What It Does + +`if consteval` detects whether the current execution context is compile-time evaluation. +The `if consteval` branch is evaluated only during constant evaluation, permitting the use of `consteval` functions and compile-time-only constructs. +The `else` branch is evaluated during runtime execution. + +## Why It Matters + +Certain algorithms require distinct implementations for compile-time versus runtime contexts. For example, a portable `constexpr` loop for constant evaluation versus a hardware-optimized intrinsic for runtime execution. `if consteval` provides compile-time context dispatch without template specialization or tag dispatching. + +## Example + +```cpp +#include +#include +#include + +constexpr int count_bits(unsigned int n) { + if consteval { + // Compile-time: use a simple loop + auto count = 0; + while (n) { + count += n & 1; + n >>= 1; + } + return count; + } else { + // Runtime: use hardware instruction + return std::popcount(n); + } +} + +int main() { + constexpr auto compile_time = count_bits(0b10110); // 3 + + auto some_runtime_value = 0b10110; + auto runtime = count_bits(some_runtime_value); + + std::println("compile_time: {}", compile_time); + std::println("runtime: {}", runtime); +} +``` diff --git a/content/if-constexpr.md b/content/if-constexpr.md new file mode 100644 index 0000000..59fe869 --- /dev/null +++ b/content/if-constexpr.md @@ -0,0 +1,55 @@ +--- +execute: true +--- + +## What It Does + +`if constexpr` evaluates a boolean condition at compile time and discards the non-selected branch. +Statements within the discarded branch are not instantiated, removing the requirement for them to be valid for the given template arguments. +This mechanism enables type-dependent code selection without runtime overhead. + +## Why It Matters + +Prior to `if constexpr`, type-dependent branching required SFINAE or tag dispatching, both utilizing indirect syntactic patterns. `if constexpr` enables compile-time branch selection using standard conditional syntax within the same function body. + +## Example + +```cpp +#include +#include +#include +#include + +template +auto stringify(const T& value) { + if constexpr (std::is_arithmetic_v) { + return std::to_string(value); + } else if constexpr (std::is_same_v) { + return value; + } else { + return std::string("[object]"); + } +} + +// Helper to detect reserve() capability +template +struct has_reserve : std::false_type {}; + +template +struct has_reserve().reserve(0))>> : std::true_type {}; + +template +void process(T& container) { + if constexpr (has_reserve::value) { + container.reserve(100); // only for containers with reserve() + } + // ... rest of processing +} + +int main() { + using namespace std::string_literals; + std::println("{}", stringify(42)); // "42" + std::println("{}", stringify(3.14)); // "3.140000" + std::println("{}", stringify("hello"s)); // "hello" +} +``` diff --git a/content/inplace-vector.md b/content/inplace-vector.md new file mode 100644 index 0000000..2449e9b --- /dev/null +++ b/content/inplace-vector.md @@ -0,0 +1,38 @@ +--- +execute: true +--- + +## What It Does + +`std::inplace_vector` is a dynamically-resizable sequence container with a fixed maximum capacity `N`. +All elements are stored within the object itself; no dynamic memory allocation is performed. +The interface matches `std::vector`. However, exceeding the capacity results in `std::bad_alloc` being thrown. + +## Why It Matters + +In environments where heap allocation is unavailable or prohibited, including embedded systems, real-time contexts, or latency-critical paths, a fixed-capacity container with automatic storage is required. `std::inplace_vector` provides `std::vector`-compatible operations without dynamic memory allocation. + +## Example + +```cpp +#include +#include + +int main() { + // Can hold up to 10 ints, stored inline + auto numbers = std::inplace_vector(); + + numbers.push_back(1); + numbers.push_back(2); + numbers.push_back(3); + + // Works like std::vector + for (auto n : numbers) { + std::println("{}", n); + } + + numbers.resize(5, 0); // {1, 2, 3, 0, 0} + + // numbers.resize(20); // throws std::bad_alloc - exceeds capacity +} +``` diff --git a/content/jthread.md b/content/jthread.md new file mode 100644 index 0000000..c93e339 --- /dev/null +++ b/content/jthread.md @@ -0,0 +1,44 @@ +--- +execute: true +--- + +## What It Does + +`std::jthread` is a thread wrapper that automatically joins upon destruction. +It incorporates cooperative cancellation through `std::stop_token`, enabling threads to query stop requests and terminate execution accordingly. + +## Why It Matters + +A `std::thread` (**not** `std::jthread`) destroyed without a prior call to `join()` or `detach()` invokes `std::terminate`. Manual stop flag implementations require explicit synchronization. `std::jthread` addresses both concerns: its destructor issues a stop request and performs an automatic join, while `std::stop_token` provides a standardized cancellation interface. + +## Example + +```cpp +#include +#include +#include +#include + +void worker(std::stop_token token) { + while (!token.stop_requested()) { + std::println("Working..."); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + std::println("Stopping gracefully"); +} + +int main() { + // open a scope + { + auto t = std::jthread(worker); // starts thread + + std::this_thread::sleep_for(std::chrono::milliseconds(350)); + + t.request_stop(); // ask thread to stop + // destructor joins automatically + } + + std::println("Thread finished"); +} +``` diff --git a/content/lambdas.md b/content/lambdas.md new file mode 100644 index 0000000..a4610eb --- /dev/null +++ b/content/lambdas.md @@ -0,0 +1,44 @@ +--- +execute: true +--- + +## What It Does + +Lambda expressions define anonymous function objects inline. They capture variables from the enclosing scope for use within the function body, and are employed in callbacks, algorithms, and contexts requiring locally-scoped function objects. + +The syntax `[captures](parameters) { body }` specifies which enclosing variables are accessible within the lambda's scope. + +## Why It Matters + +Prior to lambdas, algorithms such as `std::sort()` with custom comparison logic required the definition of separate function objects or free functions. Lambdas enable the definition of comparison logic at the call site, placing implementation near the usage context. + +## Example + +```cpp +#include +#include +#include + +int main() { + auto nums = std::vector{3, 1, 4, 1, 5, 9, 2, 6}; + + // Simple lambda in algorithm + std::ranges::sort(nums, [](int a, int b) { + return a > b; + }); + + // Capture by reference + auto sum = 0; + std::for_each(nums.begin(), nums.end(), [&sum](int n) { sum += n; }); + std::println("Sum: {}", sum); + + // Capture by value + auto factor = 10; + auto scale = [factor](int n) { return n * factor; }; + std::println("{}", scale(5)); // 50 + + // Capture all by reference [&] or by value [=] + auto printer = [&]() { std::println("Sum is {}", sum); }; + printer(); +} +``` diff --git a/content/likely-unlikely.md b/content/likely-unlikely.md new file mode 100644 index 0000000..13445ff --- /dev/null +++ b/content/likely-unlikely.md @@ -0,0 +1,52 @@ +--- +execute: false +show_assembly: true +--- + +## What It Does + +The `[[likely]]` and `[[unlikely]]` attributes indicate to the compiler the relative probability +of branch execution. The compiler may utilize these attributes to optimize code layout and branch +prediction hints. + +## Why It Matters + +Modern CPU architectures employ branch prediction; mispredictions incur significant pipeline stalls. +These attributes enable the compiler to order generated code such that the predicted-hot path falls +through sequentially while the predicted-cold path requires a branch instruction, reducing branch +misprediction penalties. + +## Example + +```cpp +#include + +int process(int value) { + if (value > 0) [[likely]] { + // This branch is expected to be taken most often + return value * 2; + } else [[unlikely]] { + // Rare case: error handling + std::println("Warning: non-positive value"); + return 0; + } +} + +int handle_success() { return 1; } +int handle_not_found() { return 2; } +int handle_server_error() { return 3; } +int handle_unknown() { return 4; } + +int parse_status(int status) { + switch (status) { + [[likely]] case 200: + return handle_success(); + case 404: + return handle_not_found(); + [[unlikely]] case 500: + return handle_server_error(); + default: + return handle_unknown(); + } +} +``` diff --git a/content/make-unique.md b/content/make-unique.md new file mode 100644 index 0000000..3595a17 --- /dev/null +++ b/content/make-unique.md @@ -0,0 +1,40 @@ +--- +execute: true +--- + +## What It Does + +`std::make_unique()` constructs a `std::unique_ptr` by allocating storage, constructing the object in-place, and wrapping the pointer in a `unique_ptr` in a single operation. Arguments are forwarded to the object's constructor. + +## Why It Matters + +Prior to `make_unique`, `std::unique_ptr(new T(...))` was the typical pattern. When multiple allocations occur in a single expression and one throws an exception, memory leaks occur because the corresponding `unique_ptr` destructor is not yet responsible for the allocated object. `make_unique` eliminates this exception-safety gap while reducing verbosity. + +## Example + +```cpp +#include +#include +#include + +struct Player { + std::string name; + int score; + Player(std::string n, int s) : name(std::move(n)), score(s) {} +}; + +int main() { + // Create unique_ptr directly + auto player = std::make_unique("Alice", 100); + std::println("{}: {}", player->name, player->score); + + // For arrays + auto numbers = std::make_unique(10); // array of 10 ints + numbers[0] = 42; + + // Compare to raw new (don't do this) + // std::unique_ptr p(new Player("Bob", 50)); + + // No explicit delete needed - RAII handles it +} +``` diff --git a/content/mdspan.md b/content/mdspan.md new file mode 100644 index 0000000..5d4f3f0 --- /dev/null +++ b/content/mdspan.md @@ -0,0 +1,39 @@ +--- +execute: true +--- + +## What It Does + +`std::mdspan` is a non-owning view over contiguous data providing multidimensional array access semantics. It encapsulates a pointer to data with layout mapping that translates multidimensional indices to memory offsets. The layout policy determines the memory access pattern (row-major or column-major). + +## Why It Matters + +Scientific computing, image processing, and linear algebra require manipulation of multidimensional data. Prior to `mdspan`, multidimensional access required manual pointer arithmetic (error-prone and opaque) or external matrix libraries. `mdspan` provides a zero-overhead abstraction for multidimensional views without dynamic allocation or indirection. + +## Example + +```cpp +#include +#include +#include + +int main() { + auto data = std::vector(12); // 12 elements + + // View as 3x4 matrix + auto matrix = std::mdspan(data.data(), 3, 4); + + // Fill with row*10 + col + for (auto row = 0uz; row < matrix.extent(0); ++row) { + for (auto col = 0uz; col < matrix.extent(1); ++col) { + matrix[row, col] = row * 10 + col; + } + } + + std::println("Element at (1,2): {}", matrix[1, 2]); // 12 + + // Works with any contiguous data + int raw[6] = {1, 2, 3, 4, 5, 6}; + auto vec = std::mdspan(raw, 2, 3); // 2x3 view +} +``` diff --git a/content/modules.md b/content/modules.md new file mode 100644 index 0000000..d85e359 --- /dev/null +++ b/content/modules.md @@ -0,0 +1,43 @@ +--- +show_only: true +--- + +## What It Does + +Modules provide an encapsulation mechanism that replaces textual inclusion of header files with explicit interface declarations. An `export` declaration specifies the module's public interface; `import` declarations make these declarations available to consuming translation units. Modules are compiled once, cached as binary metadata, and provide namespace isolation that prevents macro leakage and implementation detail exposure. + +## Why It Matters + +Header files require textual re-parsing for each translation unit, impose macro pollution and include-order dependencies, and expose implementation details through textual inclusion. Modules eliminate redundant parsing via precompiled metadata, enforce interface boundaries at the language level, and improve build performance through on-demand symbol loading. + +## Example + +```cpp +// math.ixx (module interface) +export module math; + +export int add(int a, int b) { + return a + b; +} + +export namespace geometry { + double circle_area(double radius) { + return 3.14159 * radius * radius; + } +} + +// Internal helper - not exported +int internal_helper() { return 42; } +``` + +```cpp +// main.cpp +import math; +import std; + +int main() { + std::println("{}", add(2, 3)); + std::println("{}", geometry::circle_area(5.0)); + // internal_helper(); // Error: not exported +} +``` diff --git a/content/multidim-subscript.md b/content/multidim-subscript.md new file mode 100644 index 0000000..41ca319 --- /dev/null +++ b/content/multidim-subscript.md @@ -0,0 +1,42 @@ +--- +execute: true +--- + +## What It Does + +The subscript operator `operator[]` accepts multiple arguments. The syntax `m[i, j]` performs direct multidimensional indexing without chained bracket operations or function call syntax. Within brackets, the comma functions as a parameter separator rather than the comma operator. + +## Why It Matters + +Chained subscript syntax `[i][j]` requires intermediate proxy objects with indirection overhead. Multi-argument subscript enables direct index calculation within a single function call. The syntax corresponds to standard mathematical notation for matrix and tensor element access. + +## Example + +```cpp +#include +#include + +template +class Matrix { + std::vector data_; + size_t rows_, cols_; + +public: + Matrix(size_t rows, size_t cols) + : data_(rows * cols), rows_(rows), cols_(cols) {} + + T& operator[](size_t row, size_t col) { + return data_[row * cols_ + col]; + } + + const T& operator[](size_t row, size_t col) const { + return data_[row * cols_ + col]; + } +}; + +int main() { + auto m = Matrix(3, 4); + m[1, 2] = 42; + std::println("m[1,2] = {}", m[1, 2]); +} +``` diff --git a/content/optional-ref.md b/content/optional-ref.md new file mode 100644 index 0000000..822ec99 --- /dev/null +++ b/content/optional-ref.md @@ -0,0 +1,51 @@ +--- +execute: true +--- + +## What It Does + +`std::optional` is a specialization of `std::optional` containing either a reference to an object +of type `T` or no value. Prior to its introduction, `std::optional` supported only value semantics, +necessitating workarounds such as raw pointers or `std::reference_wrapper` for optional reference +semantics. + +## Why It Matters + +Functions returning optional references to existing objects previously relied on raw pointers +(`nullptr` for absence) or `std::optional>` (verbose wrapper). +`std::optional` encodes optional reference semantics directly in the type system with +consistent `std::optional` interface semantics. + +## Example + +```cpp +#include +#include +#include +#include + +using namespace std::string_literals; + +std::optional find_by_name( + std::vector& names, + std::string_view target +) { + for (auto& name : names) { + if (name == target) { + return name; // returns reference to actual element + } + } + + return {}; // not found, return empty optional +} + +int main() { + auto names = std::vector{"Alice"s, "Bob"s, "Charlie"s}; + + if (auto found = find_by_name(names, "Bob")) { + *found = "Robert"; // modifies the original vector element + } + + std::println("names: {:n}", names); +} +``` diff --git a/content/optional.md b/content/optional.md new file mode 100644 index 0000000..05a2255 --- /dev/null +++ b/content/optional.md @@ -0,0 +1,51 @@ +--- +execute: true +--- + +## What It Does + +`std::optional` is a class template containing either a value of type `T` or no value. +It provides a type-safe representation of an optional value without pointer semantics, +sentinel values, or output parameters. Member functions `has_value()` and `value()` provide +value checking and access, with `value()` throwing `std::bad_optional_access` if no value is present. + +## Why It Matters + +Nullable pointers shift null-check responsibility to each use site without compile-time enforcement. +Sentinel values overload the value domain of a type and require convention-based interpretation. +`std::optional` encodes value presence or absence in the type itself, enabling static analysis +and compile-time verification of access patterns. + +## Example + +```cpp +#include +#include +#include +#include + +std::optional find_user(int id) { + static auto users = std::map{{1, "Alice"}, {2, "Bob"}}; + + auto it = users.find(id); + if (it != users.end()) { + return it->second; + } + + return std::nullopt; // no value +} + +int main() { + if (auto user = find_user(1)) { + std::println("Found: {}", *user); + } + + // Or use value_or for a default + std::println("{}", find_user(99).value_or("Unknown")); + + auto maybe_number = std::optional(); + std::println("Has value: {}", maybe_number.has_value()); // false + maybe_number = 42; + std::println("Value: {}", *maybe_number); // 42 +} +``` diff --git a/content/p0330_literal_suffix_size_t.md b/content/p0330_literal_suffix_size_t.md deleted file mode 100644 index 621c704..0000000 --- a/content/p0330_literal_suffix_size_t.md +++ /dev/null @@ -1,16 +0,0 @@ -A core language feature that adds a literal suffix for `size_t` and its associated -signed type. - -Example: - -``` -// Instead of writing: -auto num = size_t(0); -auto diff = ptrdiff_t(0); - -// You can now write: -auto num = 0uz; // num is of type size_t -auto diff = 0t; // diff is of type ptrdiff_t -``` - -Note that the literal suffix `zu` is also possible and equivalent to `uz`. diff --git a/content/p1967_embed.md b/content/p1967_embed.md deleted file mode 100644 index 749a5b0..0000000 --- a/content/p1967_embed.md +++ /dev/null @@ -1,24 +0,0 @@ -This feature adds the `#embed` preprocessor directive that allows you to include -the contents of a file as a byte array (binary resource inclusion). - -Example: - -``` -// Include the contents of my_image.png: -constexpr uint8_t my_image_data[] = { - #embed "my_image.png" -}; - -// Example function that loads an image from memory: -Image load_image(std::span data); - -// ... -auto image = load_image(my_image_data); -``` - ---- - -### Related pages: - -- [GCC documentation](https://gcc.gnu.org/onlinedocs/cpp/Binary-Resource-Inclusion.html) -- [cppreference](https://en.cppreference.com/w/c/preprocessor/embed) diff --git a/content/p2169_placeholder_with_no_name.md b/content/p2169_placeholder_with_no_name.md deleted file mode 100644 index 37262ce..0000000 --- a/content/p2169_placeholder_with_no_name.md +++ /dev/null @@ -1,35 +0,0 @@ -This feature allows you to use `_` as a placeholder name multiple times, for example in variable -declarations and structured bindings. - -Previously, `_` was treated as an identifier, meaning it could only be used once within a scope. - -With this feature, the following is possible, all within the same scope: - -``` -// Resource guards often have no name, since they -// only exist to perform cleanup at the end of a scope. -auto _ = std::lock_guard(mutex); - - -// Sometimes we're not interested in all elements of a tuple. -auto [x, y, _] = f(); - - -// Lambdas may not need to use an argument that is passed to them. -// Using _ marks the parameter with [[maybe_unused]] implicitly. -callback([&](auto _) { - something_that_does_not_use_the_argument(x, y); -}); - - -// Alternative to std::views::values: -for (const auto& [_, value] : map) { - std::println("value: {}", value); -} -``` - -Using the `_` placeholder implicitly gives the declaration a `[[maybe_unused]]` attribute. - -``` -auto _ = f(); // Equivalent to [[maybe_unused]] auto _ = f(); -``` diff --git a/content/p2300_execution.md b/content/p2300_execution.md deleted file mode 100644 index f370cfa..0000000 --- a/content/p2300_execution.md +++ /dev/null @@ -1,4 +0,0 @@ - -### Related libraries: - -- [bemanproject/execution (GitHub)](https://github.com/bemanproject/execution) diff --git a/content/p2546_debugging.md b/content/p2546_debugging.md deleted file mode 100644 index d95f719..0000000 --- a/content/p2546_debugging.md +++ /dev/null @@ -1,20 +0,0 @@ -This feature allows you to perform operations that target the debugger, such as triggering -a break point programmatically. - -Three new functions are added to the standard library. - -- `std::is_debugger_present() -> bool` - - Allows you to detect whether the program is running in a debugger. - -- `std::breakpoint()` - - Triggers a breakpoint in the program, which results in the debugger pausing the program's execution. - -- `std::breakpoint_if_debugging()` - - Same as `std::breakpoint()` when the program is running in a debugger; otherwise a no-op. - ---- - -### Related libraries: - -- [scottt/debugbreak (GitHub)](https://github.com/scottt/debugbreak) -- [grafikrobot/debugging (GitHub)](https://github.com/grafikrobot/debugging) diff --git a/content/p2573.md b/content/p2573.md deleted file mode 100644 index 912bdac..0000000 --- a/content/p2573.md +++ /dev/null @@ -1,32 +0,0 @@ -This feature allows you to give a reason as to why a function is deleted. - -When you try to call such a function, the compiler can issue the reason as the message. - -Example: - -``` -struct UniquePtr { - UniquePtr() = default; - - // Copy constructor - UniquePtr(const UniquePtr&) = delete( - "A UniquePtr manages unique resources and is therefore non-copyable. \ - Please std::move it instead."); -}; -``` - -Attempting to copy-construct an object of this type may result in the following compilation error: - -```raw -UniquePtr.h:5: error: call to deleted constructor of 'UniquePtr': - - A UniquePtr manages unique resources and is therefore non-copyable. - Please std::move it instead. - - UniquePtr un2 = un; - ^ ~~ - -main.cpp:1: note: 'UniquePtr' has been explicitly marked deleted here: - UniquePtr(const UniquePtr&) - ^ -``` diff --git a/content/p2589_static_subscript_operator.md b/content/p2589_static_subscript_operator.md deleted file mode 100644 index f18cea4..0000000 --- a/content/p2589_static_subscript_operator.md +++ /dev/null @@ -1,22 +0,0 @@ -This feature allows you to define `static` subscript operators, which makes it possible to -index into a type instead of an object. - -Example: - -``` -struct Test { - static int operator[](int i) { - return i * 2; - } - - // Multidimensional indexing is possible as well. - static int operator[](float f1, float f2) { - return f1 * f2; - } -}; - -// ... - -auto result = Test::operator[](10); // = 20 -auto result2 = Test::operator[](2.0f, 3.0f); // = 6 -``` diff --git a/content/p2741_static_assert_msgs.md b/content/p2741_static_assert_msgs.md deleted file mode 100644 index 3a08edc..0000000 --- a/content/p2741_static_assert_msgs.md +++ /dev/null @@ -1,50 +0,0 @@ -This feature allows you to pass a string-like object to `static_assert`'s message (second argument). - -Previously, it was limited to string literals, which could only provide limited information about the error. - -Example previously (*from proposal*): - -``` -template -constexpr bool ensure_size() { - static_assert(sizeof(T) == Expected, "Unexpected sizeof"); - return true; -} - -static_assert(ensure_size()); -``` - -```raw -error: static assertion failed due to requirement 'sizeof(S) == 1': - Unexpected sizeof -static_assert(sizeof(T) == Expected, "Unexpected sizeof"); -^ ~~~~~~~~~~~~~~~~~~~~~ -note: in instantiation of function template specialization - 'ensure_size' requested here -static_assert(ensure_size()); -^ -note: expression evaluates to '4 == 1' -static_assert(sizeof(T) == Expected, "Unexpected sizeof"); -~~~~~~~~~~^~~~~~~~~~~ -``` - ---- - -With this feature: - -``` -static_assert(sizeof(S) == 1, - fmt::format("Unexpected sizeof: expected 1, got {}", sizeof(S))); -``` - -```raw -error: static assertion failed due to requirement 'sizeof(S) == 1': - Unexpected sizeof: expected 1, got 4 -static_assert(sizeof(S) == 1, -^ ~~~~~~~~~~~~~~ -note: expression evaluates to '4 == 1' -static_assert(sizeof(S) == 1, - ~~~~~~~~~~^~~~ -``` - - diff --git a/content/p2809_trivial_infinite_loops_ub.md b/content/p2809_trivial_infinite_loops_ub.md deleted file mode 100644 index 4528579..0000000 --- a/content/p2809_trivial_infinite_loops_ub.md +++ /dev/null @@ -1,27 +0,0 @@ -This feature forbids compilers to optimize out trivial infinite loops, i.e. infinite loops without -side effects. - -Example (*from proposal*): - -``` -#include - -int main() { - while (true) - ; -} - -void unreachable() { - std::cout << "Hello world!" << std::endl; -} -``` - -This program prints `"Hello world!"`, because the optimizer detects the loop to be without side effects -and infinitely-running, and subsequently optimizing it out. This also optimizes out the `ret` instruction -(return statement of `main()`). - -Therefore, the program continues to fall through into the instructions of `unreachable()`. - -[Example on Compiler Explorer](https://godbolt.org/z/e3coW9cPE) - - diff --git a/content/p2900.md b/content/p2900.md deleted file mode 100644 index cf00ceb..0000000 --- a/content/p2900.md +++ /dev/null @@ -1,37 +0,0 @@ -This language feature allows adding contract assertions to C++ code. - -*(from the proposal)* - -There are three kinds of contract assertions: **pre**condition assertions, -**post**condition assertions and assertion statements. - -Precondition and postcondition assertions are placed on function declarations and -collectively called *function contract assertions*. - -Assertion statements are placed inside function bodies. -The following example contains all three kinds of contract assertions: - -``` -int f(const int x) - pre (x != 1) // a precondition assertion - post(r: r == x && r != 2) // a postcondition assertion - // r names the result object of f -{ - contract_assert(x != 3); // an assertion statement -} -``` - -When the predicate of an assertion evaluates to `false`, a contract -violation has been identified. This will notify the contract-violation handler, which can be -user-defined. The default contract-violation handler will terminate the program -when it is notified. - ---- - -### Related pages - -- [Contracts for C++ explained in 5 minutes](https://timur.audio/contracts_explained_in_5_mins) -- [Contracts for C++ - ACCU Conference (YouTube)](https://youtu.be/u73ZB_vml_c) -- [Experimental implementation in GCC (Compiler Explorer)](https://godbolt.org/z/fWT3rbPzf) -- [Experimental implementation in Clang (Compiler Explorer)](https://godbolt.org/z/Y85shWdae) -- [cppreference](https://en.cppreference.com/w/cpp/language/contracts.html) diff --git a/content/p2988_optional_ref.md b/content/p2988_optional_ref.md deleted file mode 100644 index d6913d2..0000000 --- a/content/p2988_optional_ref.md +++ /dev/null @@ -1,43 +0,0 @@ -This library feature adds a specialization for `std::optional` that is able to store **non-owning** references. - -With this, you're able to improve code that relies on pointers to signal nullability. - -For example: - -``` -auto find_object(std::string_view name) -> Object*; - -auto* obj = find_object("Some Object"); - -if (obj != nullptr) { - print(*obj); -} -``` - -It might not be clear to the reader whether `find_object()` always returns a valid pointer or `nullptr`. -Additionally, the caller might forget to perform a null-check for the returned value. - -With `std::optional`, this can be improved: - -``` -auto find_object(std::string_view name) -> std::optional; - -auto maybe_obj = find_object("Some Object"); - -// More apparent: value must be unwrapped first -if (maybe_obj) { - print(*maybe_obj); -} - -// Alternatively: use monadic operations -maybe_obj.and_then(print); -``` - -Together with a hardened standard library ([P3471](https://wg21.link/P3471)), `std::optional` offers -a clearer and safer alternative to raw pointers. - ---- - -### Related libraries: - -- [bemanproject/optional (GitHub)](https://github.com/bemanproject/optional) diff --git a/content/p2996_reflection.md b/content/p2996_reflection.md deleted file mode 100644 index 73b04ec..0000000 --- a/content/p2996_reflection.md +++ /dev/null @@ -1,102 +0,0 @@ -This feature adds static reflection capabilities to C++. -Static reflection allows a program to inspect its own structure at compile-time. - -This adds a new operator `^^` to the language that reflects a type and produces a reflection value -of type `std::meta::info`, which is an opaque type (implementation is only known to the compiler). - -A reflection value can be used to produce grammatical elements using a new construct called *splicer*, -which follows the form `[: refl :]`. - -In addition, a new header `` is added that provides a number of `consteval` functions to work -with `std::meta::info` objects, for example obtaining the name or members of a type. - ---- - -### Examples - -``` -constexpr auto r = ^^int; // Reflect the type int -typename[:r:] x = 42; // Same as: int x = 42; -typename[:^^char:] c = '*'; // Same as: char c = '*'; -``` - ---- - -``` -// Modify members of a struct via reflection -struct S { int i, int j; }; - -// Gets the field of S by name at compile-time: -consteval auto member_named(std::string_view name) { - auto ctx = std::meta::access_context::current(); - - // Loop through all fields of S: - for (std::meta::info field : nonstatic_data_members_of(^^S, ctx)) { - // If the name of the field matches the specified name, return the - // reflection value of the field: - if (has_identifier(field) && identifier_of(field) == name) { - return field; - } - } -} - -// ... - -auto s = S(); -s.[:member_named("j"):] = 42; // Same as: s.j = 42; -s.[:member_named("x"):] = 0; // Error (member_named("x") is not a constant). -``` - ---- - -``` -// Convert an enum to a string -template - requires std::is_enum_v -constexpr std::string_view enum_to_string(E value) { - // is_enumerable_type() checks if the type is indeed enumerable. - // For example, if E is a forward-declared enum type (incomplete) from the - // standpoint of the caller, it will not count as enumerable. - if constexpr (std::meta::is_enumerable_type(^^E)) - { - // Iterate through all the members of E: - template for (constexpr auto e : - std::define_static_array(std::meta::enumerators_of(^^E))) - { - // '[:e:]' accesses the value of the member, which is then compared against - // the value we're searching for. If they're equal, we found the member and - // can return its name. - if (value == [:e:]) { - return std::meta::identifier_of(e); - } - } - } - - return ""; -} - -// ... - -enum Color : int; // forward declare some enum (counts as incomplete) -static_assert(enum_to_string(Color(0)) == ""); // constant evaluation possible -std::println("Color 0: {}", enum_to_string(Color(0))); // prints '' - -enum Color : int { red, green, blue }; // define the enum -static_assert(enum_to_string(Color::red) == "red"); -static_assert(enum_to_string(Color(42)) == ""); -std::println("Color 0: {}", enum_to_string(Color(0))); // prints 'red' -``` - ---- - -### Related pages: - -- [P2996](https://wg21.link/P2996) -- [Gazing Beyond Reflection for C++26 (YouTube)](https://youtu.be/wpjiowJW2ks) - ---- - -### Related libraries: - -- [Neargye/magic_enum](https://github.com/Neargye/magic_enum) -- [getml/reflect-cpp](https://github.com/getml/reflect-cpp) diff --git a/content/p3471_hardening.md b/content/p3471_hardening.md deleted file mode 100644 index 64c3e9f..0000000 --- a/content/p3471_hardening.md +++ /dev/null @@ -1,64 +0,0 @@ -Standard library hardening adds certain safety checks to various types, such as -`std::vector`, `std::string` and `std::optional`. - -This prevents common cases of undefined behavior such as out-of-bounds and null pointer accesses. - -**Note** that standard library hardening is designed to also be enabled in production code. It's **not** only a debug-mode -feature. - ---- - -### Enabling - -#### With libstdc++ (GCC) - -Standard library hardening is controlled via the `_GLIBCXX_ASSERTIONS` macro, which has to be defined to enable it. -**Note** when compiling with optimizations disabled, this is enabled implicitly, by default. - -libstd++ provides multiple macros and compiler flags that enable additional checks. -For more information, see [libstdc++ Macros](https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_macros.html). - ---- - -#### With libc++ (Clang and Apple Clang) - -Standard library hardening is controlled via multiple macros, each categorized by -the amount of checks performed: - -- `_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE` -- `_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST` -- `_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE` -- `_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG` - -```cmake -# CMake example - -# Enable standard library hardening for target 'MyApp' (recommended) -target_compile_definitions(MyApp PRIVATE -D_LIBCPP_HARDENING_MODE_FAST) - -# Or enable for all targets -add_compile_definitions(-D_LIBCPP_HARDENING_MODE_FAST) -``` - -For a description of all modes, see [libc++ Hardening Modes](https://libcxx.llvm.org/Hardening.html). - ---- - -#### With MSVC STL - -Standard library hardening is controlled via the `_MSVC_STL_HARDENING` macro, which has to be set -to `1` to enable it. A value of `0` disables it. - -The MSVC STL provides multiple macros that enable additional checks, such as `_MSVC_STL_DESTRUCTOR_TOMBSTONES`. -For more information, see [MSVC STL Hardening](https://github.com/microsoft/STL/wiki/STL-Hardening). - ---- - -### Related pages: - -- [libstdc++ Macros](https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_macros.html) -- [libc++ Hardening Modes](https://libcxx.llvm.org/Hardening.html) -- [MSVC STL Hardening](https://github.com/microsoft/STL/wiki/STL-Hardening) -- [Compiler Options Hardening Guide for C and C++](https://best.openssf.org/Compiler-Hardening-Guides/Compiler-Options-Hardening-Guide-for-C-and-C++.html) -- [What’s New for C++ Developers in Visual Studio 2022 17.14](https://devblogs.microsoft.com/cppblog/whats-new-for-cpp-developers-in-visual-studio-2022-17-14/) -- [cppreference](https://en.cppreference.com/w/cpp/standard_library.html) diff --git a/content/p3533.md b/content/p3533.md deleted file mode 100644 index d5707a6..0000000 --- a/content/p3533.md +++ /dev/null @@ -1,38 +0,0 @@ -This language feature allows you to use virtual inheritance in a constexpr context. - -Example (from the proposal): - -``` -struct Superbase { - string id = "name"; -}; - -struct Common: Superbase { - int counter = 0; -}; - -struct Left : virtual Common { - int value = 0; - - constexpr const int& get_counter() const { - return Common::counter; - } -}; - -struct Right : virtual Common { - int value = 0; - - constexpr const int& get_counter() const { - return Common::counter; - } -}; - -struct Child : Left, Right { - int x = 0; - int y = 0; -}; - -constexpr auto ch = Child(); // This is now possible - -static_assert(&ch.Left::get_counter() == &ch.Right::get_counter()); -``` diff --git a/content/p3697_minor_additions_to_hardening.md b/content/p3697_minor_additions_to_hardening.md deleted file mode 100644 index 8b0d96f..0000000 --- a/content/p3697_minor_additions_to_hardening.md +++ /dev/null @@ -1,3 +0,0 @@ -This feature is a follow-up to [P3471 (Standard library hardening)](https://wg21.link/P3471) -that adds more safety checks around various types, such as `std::shared_ptr` and some iterator types. - diff --git a/content/pack-indexing.md b/content/pack-indexing.md new file mode 100644 index 0000000..83c4c26 --- /dev/null +++ b/content/pack-indexing.md @@ -0,0 +1,37 @@ +--- +execute: false +--- + +## What It Does + +Pack indexing provides direct access to individual elements of a parameter pack via the `...[N]` syntax. The index is a compile-time constant expression. This eliminates the need for recursive template instantiation or `std::tuple`-based indirection to extract specific pack elements. + +## Why It Matters + +Parameter pack element access previously required recursive template metaprogramming or `std::tuple_element` with helper types. Pack indexing provides direct compile-time access to the Nth element of a type or value pack without auxiliary template machinery or runtime overhead. + +## Example + +```cpp +#include + +template +struct TypeList { + static constexpr size_t size = sizeof...(Ts); + + template + using at = Ts...[N]; // direct access to Nth type +}; + +template +constexpr auto get(size_t index) { + constexpr auto arr = std::array{Values...}; + return arr[index]; +} + +// Usage +using List = TypeList; +using Second = List::at<1>; // double + +static_assert(std::is_same_v); +``` diff --git a/content/placeholder-variable.md b/content/placeholder-variable.md new file mode 100644 index 0000000..1e566f7 --- /dev/null +++ b/content/placeholder-variable.md @@ -0,0 +1,34 @@ +--- +show_only: true +--- + +## What It Does + +The identifier `_` serves as a placeholder variable name indicating intentional value discard. +Unlike ordinary variables, multiple declarations of `_` within the same scope do not constitute +redeclaration errors; each occurrence denotes an independent placeholder. + +## Why It Matters + +Certain programming patterns require binding a value solely for side effects (e.g. RAII lock guards) +or structured binding decomposition where only specific elements are utilized. +The placeholder `_` explicitly denotes intentionally unused bindings and suppresses compiler warnings for unused entities. + +## Example + +```cpp +#include + +std::tuple get_record(); + +void process() { + // Only care about the string, ignore the rest + auto [_, name, _] = get_record(); + + // Lock guard - we need the side effect, not the object + auto _ = std::lock_guard(mutex); + + // Ignore return value explicitly + auto _ = some_function_with_nodiscard_return(); +} +``` diff --git a/content/print.md b/content/print.md new file mode 100644 index 0000000..0adaadb --- /dev/null +++ b/content/print.md @@ -0,0 +1,52 @@ +--- +execute: true +--- + +## What It Does + +`std::print()` and `std::println()` perform type-safe formatted output using the same format +string syntax as `std::format()`. Output is written directly to `stdout` (or an optional +output stream) without `std::ostream` state management. Format string parsing occurs at compile +time with type verification of arguments. + +## Why It Matters + +`std::cout` relies on stream insertion operators, maintains mutable global state +(`std::ios_base` flags), and has incomplete Unicode handling. `printf()` provides +format-string syntax but without type safety (undefined behavior on mismatch). +`std::print()` combines format-string notation with compile-time type checking and +proper Unicode support. + +## Example + +```cpp +#include +#include +#include +#include + +using namespace std::string_literals; + +int main() { + // Basic printing + std::println("Hello, {}!", "world"); + + // Formatted numbers + std::println("hex: {:#x}, binary: {:#b}", 255, 255); + + // Containers work directly + auto nums = std::vector{1, 2, 3}; + std::println("numbers: {}", nums); + std::println("numbers without brackets: {:n}", nums); + + auto ages = std::map{ + {"Alice"s, 30}, + {"Bob"s, 25}, + }; + + std::println("ages: {}", ages); // {"Alice": 30, "Bob": 25} + + // Print blank line + std::println(); +} +``` diff --git a/content/println-blank-lines.md b/content/println-blank-lines.md new file mode 100644 index 0000000..54c5135 --- /dev/null +++ b/content/println-blank-lines.md @@ -0,0 +1,43 @@ +## What It Does + +`std::println()` gains overloads that accept no format string argument, allowing direct output of a blank line. Three new signatures are provided: +`std::println()` writes to `stdout`, `std::println(std::FILE*)` writes to a +C file stream, and `std::println(std::ostream&)` writes to a C++ +output stream. Each outputs a single newline character. + +## Why It Matters + +Before this addition, printing a blank line required passing an empty format +string: `std::println("")`. This works but involves format string parsing +machinery for what is conceptually a parameterless operation. +The zero-argument overloads express the intent directly and allow +implementations to optimize the common case of outputting a newline. + +## Example + +```cpp +#include +#include + +int main() { + std::println("Section 1"); + std::println("Content here..."); + + std::println(); // Blank line to stdout + + std::println("Section 2"); + + // Write blank line to a file + auto file = std::fopen("output.txt", "w"); + std::println(file, "Line 1"); + std::println(file); + std::println(file, "Line 2"); + std::fclose(file); + + // Write blank line to an ostream + auto ofs = std::ofstream("log.txt"); + std::println(ofs, "Entry 1"); + std::println(ofs); + std::println(ofs, "Entry 2"); +} +``` diff --git a/content/ranges-to.md b/content/ranges-to.md new file mode 100644 index 0000000..001d148 --- /dev/null +++ b/content/ranges-to.md @@ -0,0 +1,47 @@ +--- +execute: true +--- + +## What It Does + +`ranges::to()` materializes a range into a concrete container of the specified type. +It accepts any range as input and constructs the target container from the range's elements via +the container's range constructor or iterator pair insertion. + +## Why It Matters + +Range adaptors produce lazy views computed on demand during iteration. Many interfaces require +materialized containers with owned storage. Converting views to containers previously required +explicit construction with iterator pairs or `ranges::copy()` with `std::back_inserter`. +`ranges::to()` encapsulates this conversion within the range pipeline. + +## Example + +```cpp +#include +#include +#include +#include +#include + +int main() { + auto numbers = std::views::iota(1, 10) + | std::views::filter([](int n) { return n % 2 == 0; }) + | std::views::transform([](int n) { return n * n; }) + | std::ranges::to(); + + std::println("numbers: {}", numbers); + + // Convert to different container types + auto as_set = numbers | std::ranges::to(); + + std::println("as_set: {}", as_set); + + // Works with strings too + auto upper = std::string_view("hello") + | std::views::transform(::toupper) + | std::ranges::to(); + + std::println("upper: {}", upper); +} +``` diff --git a/content/ranges.md b/content/ranges.md new file mode 100644 index 0000000..5d48615 --- /dev/null +++ b/content/ranges.md @@ -0,0 +1,50 @@ +--- +execute: true +--- + +## What It Does + +Ranges encapsulate sequences of elements as single objects with `begin()` and `end()` accessors. +Range algorithms accept range objects directly rather than iterator pairs. Views provide lazy +range adaptors that compose via the pipe operator `|`, constructing transformation pipelines +evaluated on demand during iteration. + +## Why It Matters + +Iterator pairs require separate passing of two related arguments without enforcement that they +belong to the same sequence. Ranges encapsulate sequence boundaries as a single object. +View adaptors defer computation until element access, enabling efficient composition of +transformations without intermediate materialization. The pipe operator provides a left-to-right +syntax for adaptor composition. + +## Example + +```cpp +#include +#include +#include +#include + +int main() { + auto numbers = std::vector{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + // Lazy pipeline: filter, transform, take + auto result = numbers + | std::views::filter([](auto n) { return n % 2 == 0; }) + | std::views::transform([](auto n) { return n * n; }) + | std::views::take(3); + + for (auto n : result) { + std::println("{}", n); + } + + // Range algorithms work directly with containers + if (auto it = std::ranges::find(numbers, 4); it != numbers.end()) { + std::println("range contains 4"); + } + + std::ranges::sort(numbers, std::greater()); + + std::println("numbers after sorting: {}", numbers); +} +``` diff --git a/content/reflection.md b/content/reflection.md new file mode 100644 index 0000000..ea78f5e --- /dev/null +++ b/content/reflection.md @@ -0,0 +1,85 @@ +--- +execute: true +flags: "-freflection" +--- + +## What It Does + +Reflection provides compile-time introspection and manipulation of program structure. +Through the `` header, metaprogramming constructs can query type information including +non-static data members, enumerators, function signatures, and cv-qualifiers. +The resulting metadata can be used in template metaprogramming to generate code via +compile-time iteration. + +## Why It Matters + +Reflection exposes compiler-internal type information to metaprograms, eliminating the +requirement for manual boilerplate or external preprocessing tools for tasks that +consume type structure (serialization, diagnostic output, binding generation). + +## Example + +```cpp +#include +#include +#include +#include +#include + +using namespace std; +using namespace std::meta; + +enum class Color { Red, Green, Blue }; +struct Point { float x, y, z; }; + +template requires is_enum_v +consteval string_view enum_to_string(E value) { + // Get the members of enum E + constexpr auto members = define_static_array(enumerators_of(^^E)); + + for (auto i = 0uz; i < members.size(); ++i) { + if (value == extract(members[i])) { + return identifier_of(members[i]); + } + } + + return ""; +} + +template requires is_enum_v +consteval optional string_to_enum(string_view name) { + constexpr auto enums = define_static_array(enumerators_of(^^E)); + + for (auto i = 0uz; i < enums.size(); ++i) { + if (name == identifier_of(enums[i])) { + return extract(enums[i]); + } + } + + return nullopt; +} + +template +consteval auto get_member_names() { + constexpr auto ctx = access_context::unchecked(); + constexpr auto m = define_static_array(nonstatic_data_members_of(^^T, ctx)); + + auto names = array(); + + for (auto i = 0uz; i < m.size(); ++i) { + names[i] = identifier_of(m[i]); + } + + return names; +} + +int main() { + // Can use reflection results in static assertions. + static_assert(enum_to_string(Color::Red) == "Red"); + + std::println("Color::Blue's name: '{}'", enum_to_string(Color::Blue)); + + constexpr auto names = get_member_names(); + std::println("Point members: {:n}", names); +} +``` diff --git a/content/simd.md b/content/simd.md new file mode 100644 index 0000000..00204a5 --- /dev/null +++ b/content/simd.md @@ -0,0 +1,42 @@ +--- +execute: false +--- + +## What It Does + +`std::simd` provides portable SIMD (Single Instruction, Multiple Data) vector types. +Operations on `std::simd` objects are mapped by the compiler to architecture-specific +vector instructions (SSE, AVX, NEON) or scalar fallback. The type supports arithmetic +operations, comparisons, and masking with overloaded operators. + +## Why It Matters + +SIMD instruction sets provide data-level parallelism for numeric computation. +Prior to `std::simd`, portable SIMD required platform-specific intrinsics or dependence +on compiler auto-vectorization heuristics. `std::simd` decouples vectorization logic +from target architecture specifics. + +## Example + +```cpp +#include +#include + +void add_arrays(float* a, float* b, float* out, size_t n) { + using simd_t = std::native_simd; + + constexpr auto lanes = simd_t::size(); + + for (auto i = 0uz; i + lanes <= n; i += lanes) { + auto va = simd_t(a + i, std::element_aligned); + auto vb = simd_t(b + i, std::element_aligned); + auto result = va + vb; + result.copy_to(out + i, std::element_aligned); + } + + // Handle remainder + for (auto i = 0uz; i < n; ++i) { + out[i] = a[i] + b[i]; + } +} +``` diff --git a/content/source-location.md b/content/source-location.md new file mode 100644 index 0000000..e760374 --- /dev/null +++ b/content/source-location.md @@ -0,0 +1,41 @@ +--- +execute: true +--- + +## What It Does + +`std::source_location` encapsulates source code metadata +(file name, line number, column, and function name) at the point of construction. +It provides a type-safe alternative to the `__FILE__` and `__LINE__` preprocessor macros. + +## Why It Matters + +Preprocessor macros `__FILE__` and `__LINE__` expand at the point of use, +requiring manual propagation through call hierarchies. +When `std::source_location::current()` is specified as a default function parameter, +the source location of the caller is captured automatically without explicit arguments at the call site. + +## Example + +```cpp +#include +#include + +void log(std::string_view message, + std::source_location loc = std::source_location::current()) { + std::println("[{}:{}] {}: {}", + loc.file_name(), + loc.line(), + loc.function_name(), + message); +} + +void do_work() { + log("Starting work"); // automatically captures this location +} + +int main() { + log("Application started"); + do_work(); +} +``` diff --git a/content/span.md b/content/span.md new file mode 100644 index 0000000..81abb68 --- /dev/null +++ b/content/span.md @@ -0,0 +1,48 @@ +--- +execute: true +--- + +## What It Does + +`std::span` is a non-owning view over a contiguous sequence of elements. +It abstracts any contiguous storage (arrays, vectors, pointers with size) without copying. +The number of elements may be specified at compile time (`std::span`) or +determined at runtime (`std::span`). + +## Why It Matters + +Functions accepting pointer and size parameters lack bounds enforcement. +Functions accepting `std::vector&` are constrained to that specific container type. +`std::span` abstracts any contiguous range while carrying its extent, decoupling +interfaces from concrete container implementations. + +## Example + +```cpp +#include +#include +#include +#include + +// Works with any contiguous data +void process(std::span data) { + std::println("{:n}", data); + std::println("Size: {}", data.size()); +} + +int main() { + int arr[] = {1, 2, 3, 4, 5}; + auto vec = std::vector{6, 7, 8}; + auto stdArr = std::array{9, 10, 11}; + + process(arr); // C array + process(vec); // vector + process(stdArr); // std::array + + // Subspans + auto s = std::span(arr); + std::println("first two: {:n}", s.first(2)); + std::println("last two: {:n}", s.last(2)); + std::println("middle: {:n}", s.subspan(1, 3)); +} +``` diff --git a/content/stacktrace.md b/content/stacktrace.md new file mode 100644 index 0000000..3577abc --- /dev/null +++ b/content/stacktrace.md @@ -0,0 +1,43 @@ +--- +execute: true +flags: "-lstdc++exp" +--- + +## What It Does + +`std::stacktrace` captures the current call stack at runtime. +The resulting stack trace object provides iteration over stack frames, each containing function +names, source file paths, and line numbers. + +## Why It Matters + +Call stack inspection previously required platform-specific APIs (e.g., `execinfo.h` on POSIX, +`StackWalk64` on Windows) or third-party libraries. `std::stacktrace` provides a portable +interface to stack unwinding functionality as a standard library facility. + +## Example + +```cpp +#include +#include + +void inner_function() { + auto trace = std::stacktrace::current(); + + std::println("Stack trace:"); + for (const auto& frame : trace) { + std::println(" {} at {}:{}", + frame.description(), + frame.source_file(), + frame.source_line()); + } +} + +void outer_function() { + inner_function(); +} + +int main() { + outer_function(); +} +``` diff --git a/content/static-call-operator.md b/content/static-call-operator.md new file mode 100644 index 0000000..a9c89ed --- /dev/null +++ b/content/static-call-operator.md @@ -0,0 +1,45 @@ +--- +execute: true +--- + +## What It Does + +The function call operator `operator()` may be declared `static`. Stateless function +objects thereby avoid the implicit `this` parameter. Lambdas without captures implicitly +generate a static call operator. + +## Why It Matters + +Stateless function objects (comparators, hash functions, predicates) previously incurred +the overhead of an implicit `this` parameter despite having no instance state to access. +Static call operators eliminate this indirection. + +## Example + +```cpp +#include +#include +#include + +struct IsEven { + static bool operator()(int n) { // no 'this' needed + return n % 2 == 0; + } +}; + +int main() { + auto nums = std::vector{1, 2, 3, 4, 5, 6}; + + // Custom predicate with static operator() + auto count = std::ranges::count_if(nums, IsEven()); + std::println("Even count: {}", count); // 3 + + // Captureless lambdas benefit automatically + auto is_positive = [](int n) { return n > 0; }; + // Compiler generates: static bool operator()(int n) + + // Can call without an instance + auto result = IsEven::operator()(42); + std::println("result: {}", result); +} +``` diff --git a/content/static-subscript-operator.md b/content/static-subscript-operator.md new file mode 100644 index 0000000..a5d19a7 --- /dev/null +++ b/content/static-subscript-operator.md @@ -0,0 +1,34 @@ +--- +execute: true +--- + +## What It Does + +`static operator[]` allows the subscript operator to be declared as a static member function. +This mirrors the capability added for `operator()` in C++23 (P1169). +When declared static, the subscript operator does not receive an implicit object parameter. + +## Why It Matters + +Function objects and accessor types that hold no state still required a `this` pointer to be +passed when invoking `operator[]`. For stateless types like customization point objects or +index mappers, this implicit parameter serves no purpose but still consumes a register and +prevents certain optimizations. Declaring `operator[]` as static eliminates this overhead. + +## Example + +```cpp +#include +#include + +struct IdentityMapper { + static constexpr size_t operator[](size_t i) { + return i; + } +}; + +int main() { + std::println("{}", IdentityMapper()[42]); + std::println("{}", IdentityMapper::operator[](7)); +} +``` diff --git a/content/structured-bindings.md b/content/structured-bindings.md new file mode 100644 index 0000000..d50fd64 --- /dev/null +++ b/content/structured-bindings.md @@ -0,0 +1,61 @@ +--- +execute: true +--- + +## What It Does + +Structured bindings decompose tuples, pairs, arrays, and structs into named identifiers +within a single declaration. This mechanism extracts elements without explicit accessor +syntax (`.first`, `.second`, `std::get()`). + +## Why It Matters + +Accessing decomposable types via `.first`, `.second`, or `std::get()` obscures the +semantic meaning of each element. Structured bindings bind identifiers to components at the +declaration site, making data member intent explicit. + +## Example + +```cpp +#include +#include +#include +#include + +using namespace std::string_literals; + +struct Point { + int x; + int y; +}; + +std::tuple get_data() { + return {42, "hello", 3.14}; +} + +int main() { + // Unpack tuple + auto [id, name, value] = get_data(); + + // Iterate map with clear names + auto scores = std::map{ + {"Alice", 95}, + {"Bob", 87}, + }; + + for (const auto& [player, score] : scores) { + std::println("{}: {}", player, score); + } + + // Works with arrays + int arr[] = {1, 2, 3}; + auto [a, b, c] = arr; + + // Works with structs + auto p = Point{.x = 10, .y = 20}; + auto [x, y] = p; + + std::println("a={}, b={}, c={}", a, b, c); + std::println("x={}, y={}", x, y); +} +``` diff --git a/content/three-way-comparison.md b/content/three-way-comparison.md new file mode 100644 index 0000000..6953550 --- /dev/null +++ b/content/three-way-comparison.md @@ -0,0 +1,53 @@ +--- +execute: true +--- + +## What It Does + +The three-way comparison operator `<=>` computes the ordering relationship between two operands in +a single operation, returning a comparison category type (`std::strong_ordering`, `std::weak_ordering`, +or `std::partial_ordering`). +The compiler can generate the six two-way comparison operators (`<`, `<=`, `>`, `>=`, `==`, `!=`) +from a defaulted `<=>` operator. + +## Why It Matters + +Defining all six comparison operators requires repetitive boilerplate that must be maintained consistently. +A defaulted `<=>` operator delegates the implementation to the compiler. +The operator also enables single-pass comparison in sorting algorithms, whereas two-way comparisons +may require multiple passes. + +## Example + +```cpp +#include +#include +#include + +struct Person { + std::string name; + int age; + + // Defaulted: compares members in order + auto operator<=>(const Person&) const = default; +}; + +struct Version { + int major, minor, patch; + + auto operator<=>(const Version&) const = default; + bool operator==(const Version&) const = default; +}; + +int main() { + auto alice = Person("Alice", 30); + auto bob = Person("Bob", 25); + + std::println("alice < bob? {}", alice < bob); // depends on name comparison + std::println("alice == bob? {}", alice == bob); // false + + auto v1 = Version(1, 2, 3); + auto v2 = Version(1, 2, 4); + std::println("v1 < v2? {}", v1 < v2); // true +} +``` diff --git a/content/trivial-infinite-loops.md b/content/trivial-infinite-loops.md new file mode 100644 index 0000000..cb4abb1 --- /dev/null +++ b/content/trivial-infinite-loops.md @@ -0,0 +1,44 @@ +--- +execute: false +show_assembly: true +--- + +## What It Does + +Trivial infinite loops (iteration statements with empty or side-effect-free bodies) are no +longer undefined behavior. A loop such as `while (true) {}` has defined semantics; the compiler +cannot assume such loops terminate. + +## Why It Matters + +Prior to this change, trivial infinite loops had undefined behavior, permitting compilers to optimize +them under the assumption that they would not execute indefinitely. This transformation eliminated +valid patterns such as spinlocks and interrupt-driven event loops. +The change guarantees that such loops are preserved in the generated code. + +## Example + +```cpp +#include + +auto ready = std::atomic(false); + +void wait_for_signal() { + // This is now guaranteed to actually loop + // Previously, compilers could optimize this away + while (!ready.load(std::memory_order_relaxed)) { + // busy wait + } +} + +void embedded_main_loop() { + while (true) { + // Process events - guaranteed to run forever + // Compiler cannot assume this loop terminates + } +} + +int main() { + return 0; +} +``` From 3dee92b024812f887607630fbc1b946dee13aa3b Mon Sep 17 00:00:00 2001 From: Cem Dervis Date: Sat, 31 Jan 2026 01:13:04 +0100 Subject: [PATCH 2/2] Link features with new explanations --- features_cpp17.yaml | 1 + features_cpp20.yaml | 1 + features_cpp23.yaml | 17 +++++++++++++++-- features_cpp26.yaml | 28 +++++++++++++++++----------- 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/features_cpp17.yaml b/features_cpp17.yaml index ea2c82b..5ee3f76 100644 --- a/features_cpp17.yaml +++ b/features_cpp17.yaml @@ -305,6 +305,7 @@ features: - desc: "Proposed wording for structured bindings" paper: P0217 summary: "Decompose objects into named variables." + content: structured-bindings.md support: - GCC 7 - Clang 4 diff --git a/features_cpp20.yaml b/features_cpp20.yaml index 73ef0e1..b07a9a8 100644 --- a/features_cpp20.yaml +++ b/features_cpp20.yaml @@ -118,6 +118,7 @@ features: - desc: "Three-way comparison operator (`operator<=>`)" paper: P0515 summary: 'The "spaceship operator" for simplified comparisons.' + content: three-way-comparison.md support: - GCC 10 - Clang 8 (partial) diff --git a/features_cpp23.yaml b/features_cpp23.yaml index 18fb827..4130fc0 100644 --- a/features_cpp23.yaml +++ b/features_cpp23.yaml @@ -11,7 +11,7 @@ features: ftm: - name: __cpp_size_t_suffix value: 202011L - content: p0330_literal_suffix_size_t.md + content: literal-suffix-size-t.md - desc: "_Down with ()!_ (relax `()` rules for lambdas)" paper: P1102 @@ -25,6 +25,7 @@ features: - desc: "`if consteval`" paper: P1938 summary: "Detect at compile time whether code is being constant-evaluated." + content: if-consteval.md support: - GCC 12 - Clang 14 @@ -89,6 +90,7 @@ features: - desc: "Explicit object member functions (deducing `this`)" paper: P0847 summary: 'Member functions can now have an explicit "self" parameter, enabling perfect forwarding and CRTP simplification.' + content: deducing-this.md support: - GCC 14 - Clang 18 @@ -171,6 +173,7 @@ features: - desc: "Multidimensional subscript operator (`operator[x, y]`)" paper: P2128 summary: "`operator[]` can now take multiple arguments." + content: multidim-subscript.md support: - GCC 12 - Clang 15 @@ -259,6 +262,7 @@ features: - desc: "`static operator()` (static call operator)" paper: P1169 summary: "Call operators can now be static." + content: static-call-operator.md support: - GCC 13 - Clang 16 @@ -301,6 +305,7 @@ features: - desc: "`static operator[]`" paper: P2589 + content: static-subscript-operator.md support: - GCC 13 - Clang 16 @@ -309,7 +314,6 @@ features: ftm: - name: __cpp_multidimensional_subscript value: 202211L - content: p2589_static_subscript_operator.md - desc: "Permitting static `constexpr` variables in `constexpr` functions" paper: P2647 @@ -417,6 +421,7 @@ features: - desc: "`char8_t` compatibility and portability fix" paper: P2513 + content: char8-t-compatibility.md support: - GCC 13 - Clang 16 @@ -428,6 +433,7 @@ features: - desc: "``" paper: P0881 + content: stacktrace.md lib: true support: - GCC 12 (partial) @@ -755,6 +761,7 @@ features: paper: - P0323 - P2549 + content: expected.md lib: true support: - GCC 12 @@ -827,6 +834,7 @@ features: - desc: "Conversions from ranges to containers (`ranges::to()`)" paper: P1206 + content: ranges-to.md lib: true support: - GCC 14 (partial) @@ -918,6 +926,7 @@ features: - P2604 - P2613 - P2763 + content: mdspan.md lib: true support: - GCC 16 @@ -946,6 +955,7 @@ features: - desc: "`std::flat_map` (``)" paper: P0429 + content: flat-map.md lib: true support: - GCC 15 @@ -1076,6 +1086,7 @@ features: paper: - P2093 - P2539 + content: print.md lib: true support: - GCC 14 @@ -1298,6 +1309,7 @@ features: paper: - P2502 - P2787 + content: generator.md lib: true support: - GCC 14 @@ -1378,6 +1390,7 @@ features: - desc: "`views::enumerate()`" paper: P2164 + content: enumerate.md lib: true support: - GCC 13 diff --git a/features_cpp26.yaml b/features_cpp26.yaml index 7e54c74..5083a99 100644 --- a/features_cpp26.yaml +++ b/features_cpp26.yaml @@ -9,7 +9,7 @@ features: ftm: - name: __cpp_impl_reflection value: 202506L - content: p2996_reflection.md + content: reflection.md - desc: "Annotations for Reflection" paper: P3394 @@ -59,12 +59,12 @@ features: - desc: "`constexpr` virtual inheritance" paper: P3533 + content: constexpr-virtual-inheritance.md support: - GCC 16 ftm: - name: __cpp_constexpr_virtual_inheritance value: 202506L - content: p3533.md - desc: "Contracts for C++ (``)" paper: P2900 @@ -77,7 +77,7 @@ features: value: 202502L desc: "Library API is available. This is a separate macro to allow for mixing language (compiler) support with different standard library implementations, where this macro specifies the level of support for contracts by that standard library implementation." keywords: ["safety"] - content: p2900.md + content: contracts.md - desc: "Standard library hardening" paper: P3471 @@ -140,7 +140,7 @@ features: desc: "Hardened `std::vector`" value: 202502L keywords: ["safety"] - content: "p3471_hardening.md" + content: hardening.md - desc: "Minor additions to C++26 standard library hardening" paper: P3697 @@ -156,6 +156,7 @@ features: - desc: "[Pack indexing](https://en.cppreference.com/w/cpp/language/pack_indexing.html)" paper: P2662 summary: "Pack indexing provides direct access to elements in a parameter pack using `...[N]` syntax." + content: pack-indexing.md support: - GCC 15 - Clang 19 @@ -201,7 +202,7 @@ features: ftm: - name: __cpp_placeholder_variables value: 202306L - content: p2169_placeholder_with_no_name.md + content: placeholder-variable.md - desc: "`#embed` - a scannable, tooling-friendly binary resource inclusion mechanism" paper: P1967 @@ -231,7 +232,7 @@ features: ftm: - name: __cpp_pp_embed value: 202502L - content: p1967_embed.md + content: embed.md - desc: "On the ignorability of standard attributes" paper: P2552 @@ -312,6 +313,7 @@ features: - desc: '= delete("should have a reason");' paper: P2573 summary: "Deleted functions can now include a message explaining why they're deleted." + content: delete-reason.md support: - GCC 15 - Clang 19 @@ -319,7 +321,6 @@ features: ftm: - name: __cpp_deleted_function value: 202403L - content: p2573.md - desc: "Clarifying rules for brace elision in [aggregate initialization](https://en.cppreference.com/w/cpp/language/aggregate_initialization.html)" paper: P3106 @@ -347,7 +348,7 @@ features: - GCC 14 - Clang 19 - Xcode 16.3 - content: p2809_trivial_infinite_loops_ub.md + content: trivial-infinite-loops.md keywords: ["safety"] - desc: "`constexpr` placement `new`" @@ -390,7 +391,7 @@ features: value: 202506L - name: __cpp_lib_optional value: 202506L - content: p2988_optional_ref.md + content: optional-ref.md - desc: "Debugging support (``)" paper: @@ -407,7 +408,7 @@ features: - name: __cpp_lib_debugging value: 202311L keywords: ["debugger", "breakpoint"] - content: p2546_debugging.md + content: debugging.md - desc: "Variadic friends" paper: P2893 @@ -694,6 +695,7 @@ features: - desc: "Allowing exception throwing in constant-evaluation (`constexpr` exceptions)" paper: P3068 summary: "Exceptions can now be thrown and caught during constant evaluation." + content: constexpr-exceptions.md support: - GCC 16 ftm: @@ -1295,6 +1297,7 @@ features: - desc: "Printing blank lines with `std::println()`" paper: P3142 lib: true + content: println-blank-lines.md support: - GCC 14 - Clang 19 @@ -1360,11 +1363,12 @@ features: ftm: - name: __cpp_lib_senders value: 202406L - content: p2300_execution.md + content: execution.md - desc: "`std::inplace_vector`: dynamically-resizable vector with fixed capacity (``)" paper: P0843 summary: "A fixed-capacity vector that stores elements inline without heap allocation." + content: inplace-vector.md lib: true support: - GCC 16 @@ -1514,6 +1518,7 @@ features: - desc: "`std::simd` (``)" paper: P1928 summary: "Portable SIMD (Single Instruction, Multiple Data) vector types." + content: simd.md lib: true ftm: - name: __cpp_lib_simd @@ -1632,6 +1637,7 @@ features: - desc: "`std::hive` (``)" paper: P0447 summary: "A container optimized for frequent insert/erase with stable iterators." + content: hive.md lib: true ftm: - name: __cpp_lib_hive