-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconstinit-cpp20.cpp
More file actions
87 lines (67 loc) · 2.49 KB
/
constinit-cpp20.cpp
File metadata and controls
87 lines (67 loc) · 2.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#define CATCH_CONFIG_MAIN // Tells Catch2 to provide a main()
#include "../catch/catch_amalgamated.hpp"
#include "utils.h"
#include <array>
using namespace std;
// constinit ("const" is confusing; "const" is "compile time" in this context)
// - guarantees a variable is initialized at compile time (preventing the static
// initialization order fiasco), while still allowing it to be "mutated" at
// runtime — unlike `constexpr`, which locks the value permanently.
//
// - it is for a specific problem — it exists "solely" to prevent the static
// initialization order fiasco while keeping mutability, so it's targeted,
// not general-purpose - which is for global vars.
//
// constinit = compile-time init only
// (still mutable, not usable in constant expressions)
// constexpr = const + compile-time init
// (immutable, usable in const expressions)
//
// Key notes:
// - Main use case: preventing static initialization order fiasco while keeping
// mutability
// - only for variables
// - Only for static/thread-local storage duration (globals, static locals,
// thread_local)
// - Guarantees initialization happens at compile time
// (prevents silent runtime init)
// - Does not imply const — variable remains mutable
// - Does not make it usable in constant expressions
//// 1. fix init-order fiasco w/ constinit
// before
// init-order fiasco:
// - one global's initializer depends on another global from a different
// translation unit.
//
// e.g.
/*
a.cpp
int base = 10; // runtime-initialized
b.cpp
extern int base;
int derived = base * 2; // 😱 base may be 0 or 10 or garbage here!
// C++ does NOT guarantee a.cpp runs first
// derived could be 0, 20, or garbage
// now, fixed:
a.cpp
constinit int base = 10; // guaranteed at compile time
b.cpp
constexpr int derived = 10 * 2; // OK: no runtime dep on base
*/
//// 1.b. fix w/o constinit pre-cpp20
// Meyers singleton — lazy init, guaranteed safe
int& getA() {
static int a = 42; /*getValue();*/ // initialized on first call, once
return a;
}
// file2.cpp
int b = getA(); // ✓ safe — a is guaranteed initialized before b
//// 2. Mut/Immut large table initialized once at compile time
// Note:
// Use `constexpr` if immutable, `constinit` if you need to mutate it later
constinit std::array<int, 5> arr = []() {
std::array<int, 5> a; a.fill(0); return a;
}();
TEST_CASE("con-ini-2") {
REQUIRE(arr == std::array<int, 5> {0,0,0,0,0});
}