@polderlabs/bizar 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +364 -0
  3. package/cli/audit.mjs +144 -0
  4. package/cli/banner.mjs +41 -0
  5. package/cli/bin.mjs +186 -0
  6. package/cli/copy.mjs +508 -0
  7. package/cli/export.mjs +87 -0
  8. package/cli/init.mjs +147 -0
  9. package/cli/install.mjs +390 -0
  10. package/cli/plan-templates.mjs +523 -0
  11. package/cli/plan.mjs +2087 -0
  12. package/cli/prompts.mjs +163 -0
  13. package/cli/update.mjs +273 -0
  14. package/cli/utils.mjs +153 -0
  15. package/config/AGENTS.md +282 -0
  16. package/config/agents/baldr.md +148 -0
  17. package/config/agents/forseti.md +112 -0
  18. package/config/agents/frigg.md +101 -0
  19. package/config/agents/heimdall.md +157 -0
  20. package/config/agents/hermod.md +144 -0
  21. package/config/agents/mimir.md +115 -0
  22. package/config/agents/odin.md +309 -0
  23. package/config/agents/quick.md +78 -0
  24. package/config/agents/semble-search.md +44 -0
  25. package/config/agents/thor.md +97 -0
  26. package/config/agents/tyr.md +96 -0
  27. package/config/agents/vidarr.md +100 -0
  28. package/config/agents/vor.md +140 -0
  29. package/config/commands/audit.md +1 -0
  30. package/config/commands/explain.md +1 -0
  31. package/config/commands/init.md +1 -0
  32. package/config/commands/learn.md +1 -0
  33. package/config/commands/pr-review.md +1 -0
  34. package/config/commands/tailscale-serve.md +96 -0
  35. package/config/hooks/README.md +29 -0
  36. package/config/hooks/post-tool-use.md +16 -0
  37. package/config/hooks/pre-tool-use.md +16 -0
  38. package/config/opencode.json +52 -0
  39. package/config/opencode.json.template +52 -0
  40. package/config/rules/general.md +8 -0
  41. package/config/rules/git.md +11 -0
  42. package/config/rules/javascript.md +10 -0
  43. package/config/rules/python.md +10 -0
  44. package/config/rules/testing.md +10 -0
  45. package/config/skills/bizar/README.md +9 -0
  46. package/config/skills/bizar/SKILL.md +187 -0
  47. package/config/skills/cpp-coding-standards/README.md +28 -0
  48. package/config/skills/cpp-coding-standards/SKILL.md +634 -0
  49. package/config/skills/cpp-coding-standards/agents/openai.yaml +4 -0
  50. package/config/skills/cpp-coding-standards/references/concurrency.md +320 -0
  51. package/config/skills/cpp-coding-standards/references/error-handling.md +229 -0
  52. package/config/skills/cpp-coding-standards/references/memory-safety.md +216 -0
  53. package/config/skills/cpp-coding-standards/references/modern-idioms.md +282 -0
  54. package/config/skills/cpp-coding-standards/references/review-checklist.md +96 -0
  55. package/config/skills/cpp-testing/README.md +28 -0
  56. package/config/skills/cpp-testing/SKILL.md +304 -0
  57. package/config/skills/cpp-testing/agents/openai.yaml +4 -0
  58. package/config/skills/cpp-testing/references/coverage.md +370 -0
  59. package/config/skills/cpp-testing/references/framework-compare.md +175 -0
  60. package/config/skills/cpp-testing/references/host-test-for-embedded.md +499 -0
  61. package/config/skills/cpp-testing/references/mocking.md +364 -0
  62. package/config/skills/cpp-testing/references/tdd-workflow.md +308 -0
  63. package/config/skills/embedded-esp-idf/README.md +41 -0
  64. package/config/skills/embedded-esp-idf/SKILL.md +439 -0
  65. package/config/skills/embedded-esp-idf/agents/openai.yaml +4 -0
  66. package/config/skills/embedded-esp-idf/references/freertos-patterns.md +214 -0
  67. package/config/skills/embedded-esp-idf/references/host-tests.md +164 -0
  68. package/config/skills/embedded-esp-idf/references/idf-py-commands.md +157 -0
  69. package/config/skills/embedded-esp-idf/references/kconfig.md +159 -0
  70. package/config/skills/embedded-esp-idf/references/logging-discipline.md +118 -0
  71. package/config/skills/embedded-esp-idf/references/memory-and-iram.md +137 -0
  72. package/config/skills/embedded-esp-idf/references/nvs.md +121 -0
  73. package/config/skills/embedded-esp-idf/references/packed-structs.md +192 -0
  74. package/config/skills/embedded-esp-idf/scripts/idf_env.sh +47 -0
  75. package/config/skills/embedded-esp-idf/scripts/size_check.sh +77 -0
  76. package/config/skills/self-improvement/SKILL.md +64 -0
  77. package/package.json +47 -0
  78. package/templates/plan/htmx.min.js +1 -0
  79. package/templates/plan/library/bug-investigation.mdx +79 -0
  80. package/templates/plan/library/decision-record.mdx +71 -0
  81. package/templates/plan/library/feature-design.mdx +92 -0
  82. package/templates/plan/meta.json.template +8 -0
  83. package/templates/plan/plan.canvas.template +1711 -0
  84. package/templates/plan/plan.html.template +937 -0
  85. package/templates/plan/plan.mdx.template +46 -0
@@ -0,0 +1,320 @@
1
+ # Concurrency in Modern C++
2
+
3
+ This reference covers mutexes, atomics, threads, and common concurrency patterns.
4
+
5
+ ## Core Principle: No Data Races
6
+
7
+ A **data race** is simultaneous, unsynchronized access to the same memory location where at least one access is a write. It is **undefined behavior**.
8
+
9
+ Every shared variable must be:
10
+ 1. Protected by a mutex (`std::lock_guard`), OR
11
+ 2. Declared `std::atomic`, OR
12
+ 3. Never modified concurrently (read-only after initialization)
13
+
14
+ ## Mutex and Lock Guards
15
+
16
+ ### `std::lock_guard` — Simplest Guard
17
+
18
+ RAII lock — unlocks on scope exit.
19
+
20
+ ```cpp
21
+ #include <mutex>
22
+
23
+ std::mutex mtx;
24
+ int counter = 0;
25
+
26
+ void increment() {
27
+ std::lock_guard<std::mutex> lock(mtx); // acquires lock
28
+ ++counter; // safe: only one thread here at a time
29
+ } // destructor releases lock
30
+ ```
31
+
32
+ ### `std::unique_lock` — Flexible Guard
33
+
34
+ Supports deferred locking, timed locking, and manual unlock.
35
+
36
+ ```cpp
37
+ std::mutex mtx;
38
+ std::unique_lock<std::mutex> lock(mtx); // locks immediately
39
+ lock.unlock(); // temporarily unlock
40
+ lock.lock(); // relock
41
+ // ...
42
+ // automatically unlocks when lock goes out of scope
43
+
44
+ // Deferred — lock is not acquired yet
45
+ std::unique_lock<std::mutex> deferred(mtx, std::defer_lock);
46
+ // ... other code ...
47
+ deferred.lock(); // now lock
48
+ deferred.unlock(); // unlock early if needed
49
+ ```
50
+
51
+ ### `std::scoped_lock` (C++17) — Multiple Mutexes
52
+
53
+ Locks multiple mutexes without deadlock (uses the deadlock-avoidance algorithm).
54
+
55
+ ```cpp
56
+ std::mutex m1, m2, m3;
57
+
58
+ // GOOD: locks all three safely — avoids deadlock
59
+ std::scoped_lock lock(m1, m2, m3);
60
+ // ... access protected data ...
61
+ ```
62
+
63
+ ### The Deadlock Problem
64
+
65
+ Lock mutexes in a **fixed order** if using multiple `lock_guard`/`unique_lock`. Or prefer `scoped_lock`.
66
+
67
+ ```cpp
68
+ // DANGEROUS: different threads lock in opposite orders → deadlock
69
+ void transfer(Account& from, Account& to, int amount) {
70
+ std::lock_guard<std::mutex> l1(from.mtx);
71
+ std::lock_guard<std::mutex> l2(to.mtx); // deadlock if another thread does to→from
72
+ from.balance -= amount;
73
+ to.balance += amount;
74
+ }
75
+
76
+ // SAFE: scoped_lock locks both simultaneously
77
+ void transfer(Account& from, Account& to, int amount) {
78
+ std::scoped_lock lock(from.mtx, to.mtx); // no deadlock
79
+ from.balance -= amount;
80
+ to.balance += amount;
81
+ }
82
+ ```
83
+
84
+ ## Atomics
85
+
86
+ Use `std::atomic` for simple shared values where the type supports lock-free operations.
87
+
88
+ ```cpp
89
+ #include <atomic>
90
+
91
+ // Basic types
92
+ std::atomic<int> counter{0};
93
+ counter.fetch_add(1); // returns old value
94
+ counter++; // simple ops also work
95
+ int old = counter.exchange(42); // read and write
96
+
97
+ // Memory ordering
98
+ counter.store(1, std::memory_order_relaxed); // only use for counters
99
+ counter.store(1, std::memory_order_release); // release semantics
100
+ int x = counter.load(std::memory_order_acquire); // acquire semantics
101
+
102
+ // Default memory_order_seq_cst is safest and the default — use it unless you
103
+ // have measured that relaxed/acq_rel gives meaningful gains
104
+
105
+ // Boolean flag
106
+ std::atomic<bool> ready{false};
107
+ void producer() { ready.store(true, std::memory_order_release); }
108
+ void consumer() {
109
+ while (!ready.load(std::memory_order_acquire)) {
110
+ std::this_thread::yield();
111
+ }
112
+ // consume
113
+ }
114
+ ```
115
+
116
+ ## `std::atomic` with User-Defined Types
117
+
118
+ A `std::atomic<T>` requires `T` to be trivially copyable and lock-free for the operations to be available.
119
+
120
+ ```cpp
121
+ struct Point { int x; int y; };
122
+
123
+ // std::atomic<Point> — only works if Point is trivially copyable and
124
+ // the platform provides lock-free atomics for its size
125
+ static_assert(std::is_trivially_copyable_v<Point>);
126
+ static_assert(std::atomic<Point>::is_always_lock_free());
127
+ ```
128
+
129
+ ## Threads
130
+
131
+ ### `std::thread` Lifecycle — Critical Rule
132
+
133
+ **Every `std::thread` must be either joined (`.join()`) or detached (`.detach()`) before destruction.** A `std::thread` that is neither joined nor detached calls `std::terminate`.
134
+
135
+ ```cpp
136
+ #include <thread>
137
+
138
+ void background_task(int param) { /* ... */ }
139
+
140
+ // GOOD — explicit join
141
+ std::thread t(background_task, 42);
142
+ t.join(); // wait for completion
143
+
144
+ // GOOD — detached (fire and forget, but lose ability to synchronize)
145
+ std::thread t(background_task, 42);
146
+ t.detach(); // thread continues running independently
147
+
148
+ // DANGEROUS — terminate() called
149
+ {
150
+ std::thread t(background_task, 42);
151
+ // t not joined or detached here
152
+ } // std::terminate() called
153
+ ```
154
+
155
+ ### `std::jthread` (C++20) — Auto-Join
156
+
157
+ Automatically joins on destruction. Prefer `std::jthread` over `std::thread` in C++20.
158
+
159
+ ```cpp
160
+ #include <thread>
161
+
162
+ void cancellable_work(std::stop_token token) {
163
+ while (!token.stop_requested()) {
164
+ do_step();
165
+ }
166
+ }
167
+
168
+ std::jthread jt(cancellable_work);
169
+ // ... work happens in background ...
170
+ // jt destroyed here → automatically requests stop and joins
171
+ ```
172
+
173
+ ### Stop Tokens (C++20)
174
+
175
+ Gracefully stop a `jthread` without polling a flag.
176
+
177
+ ```cpp
178
+ void long_task(std::stop_token token) {
179
+ for (size_t i = 0; i < 1000; ++i) {
180
+ if (token.stop_requested()) {
181
+ std::cout << "Stopped at " << i << '\n';
182
+ return; // clean exit
183
+ }
184
+ compute_step(i);
185
+ }
186
+ }
187
+
188
+ std::jthread worker(long_task);
189
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
190
+ worker.request_stop(); // signals stop_requested()
191
+ ```
192
+
193
+ ## `std::condition_variable`
194
+
195
+ Used to block a thread until another thread signals that a condition is true.
196
+
197
+ ```cpp
198
+ #include <condition_variable>
199
+ #include <queue>
200
+
201
+ std::mutex mtx;
202
+ std::condition_variable cv;
203
+ std::queue<int> q;
204
+
205
+ void producer() {
206
+ for (int i = 0; i < 10; ++i) {
207
+ {
208
+ std::lock_guard<std::mutex> lock(mtx);
209
+ q.push(i);
210
+ }
211
+ cv.notify_one(); // wake one waiting consumer
212
+ }
213
+ }
214
+
215
+ void consumer() {
216
+ while (true) {
217
+ std::unique_lock<std::mutex> lock(mtx);
218
+ cv.wait(lock, [&] { return !q.empty() || /* done signal */; });
219
+ if (q.empty()) break; // producer done
220
+ int val = q.front();
221
+ q.pop();
222
+ lock.unlock(); // unlock while processing (optional)
223
+ // process val
224
+ }
225
+ }
226
+ ```
227
+
228
+ ## `std::future` and `std::promise`
229
+
230
+ One-shot communication from a background thread to a caller.
231
+
232
+ ```cpp
233
+ #include <future>
234
+
235
+ std::promise<int> p;
236
+ std::future<int> f = p.get_future();
237
+
238
+ std::thread t([&p]() {
239
+ // compute result
240
+ p.set_value(42);
241
+ // or on error: p.set_exception(std::make_exception_ptr(std::runtime_error("fail")));
242
+ });
243
+
244
+ int result = f.get(); // blocks until value is set
245
+ t.join();
246
+ ```
247
+
248
+ ### `std::async` — Simpler Parallelism
249
+
250
+ Launch a task asynchronously and get a future.
251
+
252
+ ```cpp
253
+ #include <future>
254
+
255
+ // LaunchPolicy::async = run in separate thread
256
+ // LaunchPolicy::deferred = lazy evaluation in calling thread
257
+ auto fut = std::async(std::launch::async, []() {
258
+ return compute_expensive_result();
259
+ });
260
+
261
+ int result = fut.get(); // blocks until ready
262
+ ```
263
+
264
+ ## Common Concurrency Mistakes
265
+
266
+ ```cpp
267
+ // WRONG: Forgetting to protect shared data
268
+ std::string shared; // data race: multiple threads write
269
+ void writer() { shared = "hello"; }
270
+ void reader() { std::cout << shared; }
271
+ std::thread t1(writer), t2(reader); // DATA RACE
272
+
273
+ // RIGHT: Protect with mutex
274
+ std::mutex mtx;
275
+ std::string shared;
276
+ void writer() {
277
+ std::lock_guard<std::mutex> lock(mtx);
278
+ shared = "hello";
279
+ }
280
+ void reader() {
281
+ std::lock_guard<std::mutex> lock(mtx);
282
+ std::cout << shared;
283
+ }
284
+
285
+ // WRONG: Passing references to local variables to threads
286
+ void bad() {
287
+ std::string s = "data";
288
+ std::thread t([&s]() { use(s); }); // reference to local — DANGER
289
+ t.detach(); // s destroyed, thread still uses it
290
+ }
291
+
292
+ // RIGHT: Move or copy into the thread
293
+ void good() {
294
+ std::string s = "data";
295
+ std::thread t([s]() { use(s); }); // copy into thread's closure
296
+ t.join(); // or jthread
297
+ }
298
+
299
+ // WRONG: Locking a mutex and calling an unknown function (potential deadlock)
300
+ // If the function tries to lock the same mutex → deadlock
301
+ void dangerous(std::mutex& m) {
302
+ std::lock_guard<std::mutex> lock(m);
303
+ call_unknown_function(); // might lock m again
304
+ }
305
+ ```
306
+
307
+ ## Summary Table
308
+
309
+ | Primitive | Use When |
310
+ |---|---|
311
+ | `std::mutex` | Protect a single variable or small critical section |
312
+ | `std::lock_guard` | Simple RAII lock (most common) |
313
+ | `std::unique_lock` | Deferred/timed locking, condition variables |
314
+ | `std::scoped_lock` | Locking multiple mutexes at once |
315
+ | `std::atomic` | Simple shared counters, flags, lock-free data |
316
+ | `std::thread` | Create a thread (remember join/detach) |
317
+ | `std::jthread` | Thread that should auto-join and support stop tokens |
318
+ | `std::condition_variable` | Block until a condition is signaled |
319
+ | `std::future`/`std::promise` | One-shot result from background task |
320
+ | `std::async` | Simple parallel task without explicit thread management |
@@ -0,0 +1,229 @@
1
+ # Error Handling in Modern C++
2
+
3
+ This reference covers when to use exceptions, error codes, or `std::expected`.
4
+
5
+ ## Decision Guide
6
+
7
+ | Scenario | Recommended Approach |
8
+ |---|---|
9
+ | Constructor failure that leaves object in invalid state | Exception |
10
+ | Expected failure (element not found, invalid input format) | `std::optional`, error code, or `std::expected` |
11
+ | Library with many failure modes | `std::error_code` or `std::expected<T, E>` |
12
+ | Performance-critical hot path where exceptions are unacceptable | Error codes |
13
+ | Impossible failure (e.g., `std::terminate`) | Let it crash |
14
+ | Memory allocation failure | `std::bad_alloc` exception (let it propagate) |
15
+
16
+ **Golden rule:** Throw exceptions for *exceptional* conditions — things that should not happen in normal operation and from which the caller cannot reasonably recover. Do not use exceptions for expected failure cases (file not found, invalid user input).
17
+
18
+ ## Exceptions
19
+
20
+ ### Throwing and Catching
21
+
22
+ ```cpp
23
+ #include <stdexcept>
24
+
25
+ class ConfigError : public std::runtime_error {
26
+ public:
27
+ explicit ConfigError(const std::string& msg) : std::runtime_error(msg) {}
28
+ };
29
+
30
+ void load_config(const std::string& path) {
31
+ std::ifstream in(path);
32
+ if (!in) {
33
+ throw ConfigError("Cannot open config: " + path);
34
+ }
35
+ // ...
36
+ }
37
+
38
+ // Caller
39
+ try {
40
+ load_config("app.cfg");
41
+ } catch (const ConfigError& e) {
42
+ std::cerr << "Config error: " << e.what() << '\n';
43
+ } catch (const std::exception& e) {
44
+ std::cerr << "Unexpected: " << e.what() << '\n';
45
+ }
46
+ ```
47
+
48
+ ### Constructor Failure
49
+
50
+ Prefer to signal constructor failure via:
51
+ 1. A `static` factory function that returns `std::optional` or `std::expected`
52
+ 2. Throwing an exception (acceptable if construction failure is truly exceptional)
53
+
54
+ ```cpp
55
+ // Approach 1: Factory returning optional
56
+ class Widget {
57
+ Widget(int param) {} // private or non-public
58
+ public:
59
+ static std::optional<Widget> create(int param) {
60
+ if (param < 0) return std::nullopt;
61
+ return Widget(param);
62
+ }
63
+ };
64
+
65
+ // Approach 2: Exception for truly unexpected failure
66
+ class NetworkConnection {
67
+ public:
68
+ NetworkConnection(const std::string& host, int port) {
69
+ if (port < 0 || port > 65535)
70
+ throw std::invalid_argument("Invalid port: " + std::to_string(port));
71
+ }
72
+ };
73
+ ```
74
+
75
+ ## Error Codes
76
+
77
+ Traditional C-style error handling via return values. Works well in performance-sensitive code and for system-level APIs.
78
+
79
+ ```cpp
80
+ #include <system_error>
81
+
82
+ enum class MyError { NotFound = 1, InvalidInput, Timeout };
83
+
84
+ // Return std::error_code — idiomatic C++ error code
85
+ std::error_code read_data(const std::string& path, std::vector<char>& out) {
86
+ std::ifstream in(path, std::ios::binary);
87
+ if (!in) return std::errc::no_such_file_or_directory;
88
+ out.assign(std::istreambuf_iterator<char>(in), {});
89
+ return {}; // empty error_code = success
90
+ }
91
+
92
+ // Usage
93
+ std::vector<char> data;
94
+ std::error_code ec = read_data("file.bin", data);
95
+ if (ec) {
96
+ std::cerr << "Read failed: " << ec.message() << '\n';
97
+ return;
98
+ }
99
+ // use data
100
+ ```
101
+
102
+ ## `std::expected<T, E>` (C++23)
103
+
104
+ The preferred way to handle expected failures in modern C++. Available in C++23; use the `std::expected` proposal or the `tl::expected` library for C++17/20.
105
+
106
+ ```cpp
107
+ // C++23 std::expected
108
+ #include <expected>
109
+
110
+ std::expected<int, std::error_code> parse_int(std::string_view s) {
111
+ int result = 0;
112
+ for (char c : s) {
113
+ if (!std::isdigit(c))
114
+ return std::unexpected(std::errc::invalid_argument);
115
+ result = result * 10 + (c - '0');
116
+ }
117
+ return result;
118
+ }
119
+
120
+ // Usage
121
+ auto opt = parse_int("123abc");
122
+ if (opt) {
123
+ std::cout << "Value: " << *opt << '\n';
124
+ } else {
125
+ std::cerr << "Parse failed: " << opt.error().message() << '\n';
126
+ }
127
+
128
+ // Transform with .and_then (monadic interface in C++23)
129
+ auto doubled = parse_int("42").transform([](int x) { return x * 2; });
130
+ // doubled == 84
131
+ ```
132
+
133
+ ### Monadic Operations (C++23)
134
+
135
+ ```cpp
136
+ // and_then — chain operations that can fail
137
+ auto result = read_config()
138
+ .and_then([](const Config& c) -> std::expected<Settings, E> {
139
+ return validate(c);
140
+ })
141
+ .transform([](const Settings& s) {
142
+ return process(s); // successful transform
143
+ });
144
+
145
+ // or_else — handle failure
146
+ auto with_fallback = read_config().or_else([](auto) {
147
+ return read_default_config();
148
+ });
149
+
150
+ // transform_error — map error type
151
+ auto with_err_msg = parse_int("abc").transform_error([](std::errc e) {
152
+ return std::string(std::strerror(std::make_error_code(e).value()));
153
+ });
154
+ ```
155
+
156
+ ## `std::optional` for Expected Absence
157
+
158
+ When the only "failure" is that a value is not present, `std::optional` is the right tool.
159
+
160
+ ```cpp
161
+ std::optional<int> find_user(const std::string& name) {
162
+ for (auto& [n, id] : user_db) {
163
+ if (n == name) return {id};
164
+ }
165
+ return std::nullopt;
166
+ }
167
+
168
+ if (auto uid = find_user("Bob")) {
169
+ std::cout << "User ID: " << *uid << '\n';
170
+ } else {
171
+ std::cout << "User not found\n";
172
+ }
173
+ ```
174
+
175
+ ## Exception Safety Guarantees
176
+
177
+ Four levels of exception safety:
178
+
179
+ | Level | Guarantee | Use When |
180
+ |---|---|---|
181
+ | **Nothrow** (`noexcept`) | Operation never throws | Move constructors, swap, essential operations |
182
+ | **Strong** | Failed op leaves state unchanged | Most business logic |
183
+ | **Basic** | Failed op leaves object valid | Destructors, `operator=` |
184
+ | **No guarantee** | Any behavior on failure | Avoid |
185
+
186
+ ```cpp
187
+ class Widget {
188
+ std::vector<int> data_;
189
+ public:
190
+ // Strong exception safety — copy-on-write pattern
191
+ void replace(std::vector<int> new_data) {
192
+ std::vector<int> tmp = std::move(new_data); // copy into temp
193
+ data_.swap(tmp); // noexcept swap
194
+ // old data_ is in tmp, destroyed when tmp goes out of scope
195
+ }
196
+
197
+ // noexcept on functions that cannot fail
198
+ void clear() noexcept { data_.clear(); }
199
+ bool empty() const noexcept { return data_.empty(); }
200
+ };
201
+ ```
202
+
203
+ ## noexcept — When to Use
204
+
205
+ Use `noexcept` on functions that **cannot** throw by design. This enables optimizations and documents API contracts.
206
+
207
+ ```cpp
208
+ // Destructors should be noexcept (the default)
209
+ class Resource {
210
+ int* ptr_ = nullptr;
211
+ public:
212
+ ~Resource() { delete[] ptr_; } // implicitly noexcept
213
+ };
214
+
215
+ // Move constructors of containers are noexcept — this matters
216
+ // A vector will use move() instead of copy() if the move ctor is noexcept
217
+ struct Important {
218
+ std::vector<int> big_data;
219
+ Important(Important&&) noexcept = default; // ensure noexcept
220
+ };
221
+
222
+ // Mark observers noexcept
223
+ template <typename T>
224
+ bool is_empty(const std::vector<T>& v) noexcept {
225
+ return v.empty(); // cannot throw — .empty() is noexcept
226
+ }
227
+ ```
228
+
229
+ **Do NOT mark a function `noexcept` if it can throw.** Misleading `noexcept` violates caller expectations and can cause `std::terminate` to be called unexpectedly.