8Expliquez les niveaux de sûreté vis-à-vis des exceptions et la manière dont `noexcept` affecte les opérations de déplacement, les conteneurs et la génération de code.
Les niveaux de sûreté vis-à-vis des exceptions décrivent ce qui reste vrai si une opération lance une exception. La garantie de base signifie que les invariants sont préservés et que les ressources ne fuient pas, même si l’état a pu changer. La garantie forte signifie une sémantique de validation ou retour arrière : en cas d’échec, l’état observable est inchangé. La garantie nothrow signifie que l’opération ne lance pas d’exception. Les techniques courantes incluent RAII, le travail sur des temporaires, copy-and-swap et l’ordonnancement des mutations de sorte que la validation ne se produise qu’après la réussite du travail susceptible de lancer. `noexcept` est un contrat : si une fonction `noexcept` lance, `std::terminate` est appelé. Il affecte aussi le code générique et les conteneurs : par exemple, pendant une réallocation, `std::vector` peut déplacer les éléments lorsque le constructeur de déplacement est `noexcept` ; sinon il peut copier, souvent via `std::move_if_noexcept`, afin de préserver les garanties d’exception. `noexcept` peut aussi aider la génération de code ou l’optimisation en réduisant les chemins nécessaires de propagation d’exceptions, mais uniquement lorsque la promesse est correcte.
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));
}
Essayer de répondre à cette question avec un coach IA
9Expliquez `std::expected` ou les types outcome équivalents comme alternatives aux exceptions pour les opérations susceptibles d’échouer.
`std::expected<T, E>` représente soit une valeur de succès `T`, soit une erreur explicite `E`, ce qui intègre l’échec au type de retour de la fonction au lieu de s’appuyer sur la propagation d’exceptions. C’est utile pour les opérations susceptibles d’échouer où les erreurs sont attendues et où les appelants doivent les gérer localement, comme l’analyse, la validation, les appels réseau, les recherches dans le stockage et les opérations backend réessayables. Le type d’erreur doit porter des informations structurées telles qu’un code d’erreur, une catégorie, la possibilité de réessayer, un message ou un mappage HTTP/RPC. Les types expected/outcome se composent en vérifiant et en propageant les erreurs et, dans les API de style C++23, peuvent utiliser des opérations monadiques comme `and_then`, `transform` et `or_else` pour enchaîner les étapes sans conditionnelles profondément imbriquées. Par rapport aux exceptions, ils rendent le flot de contrôle et les contrats d’API explicites et fonctionnent bien aux frontières sans exceptions ou d’ABI ; les exceptions peuvent toutefois rester appropriées pour des défaillances rares, non locales ou réellement exceptionnelles selon la politique du projet.
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)};
}
Essayer de répondre à cette question avec un coach IA