8Leg uit wat de niveaus van veiligheid bij uitzonderingen zijn en hoe `noexcept` van invloed is op verplaatsingsoperaties, containers en codegeneratie.
Niveaus van veiligheid bij uitzonderingen beschrijven wat geldig blijft wanneer een bewerking een uitzondering opgooit. De basisgarantie houdt in dat invarianten behouden blijven en er geen bronnen lekken, hoewel de status veranderd kan zijn. De sterke garantie betekent commit-of-rollback-semantiek: bij falen blijft de waarneembare status ongewijzigd. De nothrow-garantie betekent dat de bewerking geen uitzonderingen opgooit. Veelgebruikte technieken zijn onder meer RAII (Resource Acquisition Is Initialization), werk uitvoeren op tijdelijke objecten, copy-and-swap, en het ordenen van mutaties zodat de definitieve wijziging pas plaatsvindt nadat code die uitzonderingen kan opgooien succesvol is afgerond. `noexcept` is een contract: als een `noexcept`-functie toch een uitzondering opgooit, wordt `std::terminate` aangeroepen. Het heeft ook invloed op generieke code en containers: tijdens herallocatie kan `std::vector` bijvoorbeeld elementen verplaatsen als de verplaatsingsconstructor `noexcept` is; anders kan deze kopiëren, vaak via `std::move_if_noexcept`, om de garanties rond uitzonderingen te behouden. `noexcept` kan ook helpen bij codegeneratie of optimalisatie door het aantal benodigde paden voor de propagatie van uitzonderingen te verminderen, maar alleen wanneer deze belofte klopt.
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));
}
Probeer deze vraag te beantwoorden met een AI-begeleider
9Leg `std::expected` of vergelijkbare 'outcome'-typen uit als alternatieven voor uitzonderingen bij bewerkingen die kunnen falen.
`std::expected<T, E>` vertegenwoordigt ofwel een succesvolle waarde `T` of een expliciete fout `E`, waardoor het falen deel uitmaakt van het retourtype van de functie in plaats van te vertrouwen op de propagatie van uitzonderingen. Het is nuttig voor bewerkingen die kunnen falen waarbij fouten worden verwacht en aanroepers deze lokaal moeten afhandelen, zoals parsing, validatie, netwerkaanroepen, opslagzoekopdrachten en herhaalbare backendbewerkingen. Het fouttype moet gestructureerde informatie bevatten, zoals een foutcode, categorie, herhaalbaarheid, foutmelding of HTTP/RPC-toewijzing (RPC = Remote Procedure Call). `expected`- of `outcome`-typen kunnen worden samengesteld door fouten te controleren en door te geven, en in C++23-stijl API's kunnen ze monadische bewerkingen gebruiken zoals `and_then`, `transform` en `or_else` om stappen aaneen te schakelen zonder diep geneste conditionele logica. Vergeleken met uitzonderingen maken ze de controleflow en API-contracten expliciet, en werken ze goed over grenzen heen waar geen uitzonderingen zijn toegestaan of ABI-grenzen (Application Binary Interface). Afhankelijk van het projectbeleid kunnen uitzonderingen nog steeds geschikt zijn voor zeldzame, niet-lokale of werkelijk uitzonderlijke storingen.
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)};
}
Probeer deze vraag te beantwoorden met een AI-begeleider