Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions content/auto.md
Original file line number Diff line number Diff line change
@@ -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 <vector>
#include <map>
#include <print>

using namespace std;

int main() {
// Instead of: vector<int>::iterator it = vec.begin();
auto vec = vector{1, 2, 3, 4, 5};
auto it = vec.begin();

// Complex types become manageable
auto data = map<string, vector<int>>{
{"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));
}
```
39 changes: 39 additions & 0 deletions content/bit-cast.md
Original file line number Diff line number Diff line change
@@ -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 <bit>
#include <cstdint>
#include <print>

int main() {
// View float's bits as integer
auto f = 1.5f;
auto bits = std::bit_cast<uint32_t>(f);
std::println("1.5f as bits: {:#010x}", bits);

// Convert back
auto f2 = std::bit_cast<float>(bits);
std::println("1.5f restored from bits: {}", f2);

// Works at compile time
constexpr auto pi_bits = std::bit_cast<uint64_t>(3.14159265358979);
static_assert(pi_bits == 4614256656552045841);
}
```
27 changes: 27 additions & 0 deletions content/char8-t-compatibility.md
Original file line number Diff line number Diff line change
@@ -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 <print>

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
}
```
40 changes: 40 additions & 0 deletions content/char8-t.md
Original file line number Diff line number Diff line change
@@ -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 <string>
#include <print>

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<const char*>(greeting);
}
```
54 changes: 54 additions & 0 deletions content/concepts.md
Original file line number Diff line number Diff line change
@@ -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 `<concepts>`; 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 <concepts>
#include <print>

// Using standard concepts
template <std::integral T>
auto double_it(T value) {
return value * 2;
}

// Custom concept
template <typename T>
concept Printable = requires(T t) {
// States that the following expression must be valid for T:
std::println("{}", t);
};

template <Printable T>
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
}
```
45 changes: 45 additions & 0 deletions content/constexpr-exceptions.md
Original file line number Diff line number Diff line change
@@ -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 <stdexcept>

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);
```
44 changes: 44 additions & 0 deletions content/constexpr-virtual-inheritance.md
Original file line number Diff line number Diff line change
@@ -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 <print>

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());
}
```
55 changes: 55 additions & 0 deletions content/constexpr-virtual.md
Original file line number Diff line number Diff line change
@@ -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);
```
Loading