8Explain exception safety levels and how noexcept affects move operations, containers, and code generation.
Exception safety levels describe what remains true if an operation throws. The basic guarantee means invariants are preserved and resources are not leaked, though state may have changed. The strong guarantee means commit-or-rollback semantics: on failure, the observable state is unchanged. The nothrow guarantee means the operation does not throw. Common techniques include RAII, performing work on temporaries, copy-and-swap, and ordering mutations so commit happens only after throwing work succeeds. `noexcept` is a contract: if a `noexcept` function throws, `std::terminate` is called. It also affects generic code and containers: for example, during reallocation `std::vector` can move elements when the move constructor is `noexcept`; otherwise it may copy, often via `std::move_if_noexcept`, to preserve exception guarantees. `noexcept` can also help code generation or optimization by reducing needed exception propagation paths, but only when the promise is correct.
std::vector<T> v;
// During reallocation, an implementation may effectively do:
T* dest = allocate(new_cap);
for (T& x : old_storage) {
construct(dest++, std::move_if_noexcept(x));
}
Try answering this question with an AI coach
9Explain std::expected or equivalent outcome types as alternatives to exceptions for fallible operations.
`std::expected<T, E>` represents either a successful value `T` or an explicit error `E`, making failure part of the function’s return type instead of relying on exception propagation. It is useful for fallible operations where errors are expected and callers should handle them locally, such as parsing, validation, network calls, storage lookups, and retryable backend operations. The error type should carry structured information such as an error code, category, retryability, message, or HTTP/RPC mapping. Expected/outcome types compose by checking and propagating errors, and in C++23-style APIs can use monadic operations such as `and_then`, `transform`, and `or_else` to chain steps without deeply nested conditionals. Compared with exceptions, they make control flow and API contracts explicit and work well across exception-free or ABI boundaries; exceptions may still be appropriate for rare, non-local, or truly exceptional failures depending on project policy.
enum class Errc { BadRequest, NotFound, Timeout, DbUnavailable };
struct Error {
Errc code;
bool retryable;
std::string message;
};
std::expected<User, Error> load_user(UserId id);
HttpResponse handle(UserId id) {
auto user = load_user(id);
if (!user) {
switch (user.error().code) {
case Errc::BadRequest: return {400, user.error().message};
case Errc::NotFound: return {404, "not found"};
case Errc::Timeout: return {503, "try again"};
case Errc::DbUnavailable: return {503, "service unavailable"};
}
}
return {200, serialize(*user)};
}
Try answering this question with an AI coach