Skip to content

← Errors step 3 of 4

Medium Primitives

The promise the standard library reads

void step() noexcept;

noexcept says: nothing thrown from inside this function will escape it. It is not a request and it is not checked at compile time. If an exception does try to leave, the program calls std::terminate — no unwinding, no destructors, no handler anywhere up the stack gets a chance.

That is deliberately brutal, because the promise is not for you. It is for the code that reads it.

What reads it

std::vector needs to relocate its elements when it grows past capacity, and it wants to keep its strong guarantee: if anything throws during the growth, the vector is left exactly as it was.

Moving the elements makes that impossible. Halfway through, the old block holds moved-from husks and the new block is half-built; if the next move throws, there is nothing to roll back to, because the originals are gone.

Copying the elements makes it easy. The originals are untouched until every copy has succeeded.

So the vector asks the type:

Move constructor What vector does when it grows
noexcept moves each element
not noexcept copies each element

A missing keyword turns every reallocation of every vector of your type into a deep copy. Nothing fails, nothing warns, and the profile just looks like that. This is the single most expensive one-word omission in C++, and performance-noexcept-move-constructor is in the gate because of it.

Where the promise belongs

Move constructor / move assignment yes — the case above
swap yes — everything that provides a strong guarantee is built on a non-throwing swap
Destructors already implicitly noexcept; a throw escaping one terminates
A simple getter, size(), arithmetic yes, and it costs nothing
Anything that allocates nostd::bad_alloc is a thing

That last row is the discipline. noexcept on a function that pushes onto a vector is a promise you cannot keep, and the punishment for breaking it is std::terminate rather than an exception someone could have handled.

When in doubt, leave it off. noexcept is easy to add later and very hard to remove — it is part of the type of the function, and callers may already be branching on it.

Your task

grow is given: it appends every buffer to a vector, without reserving, so the vector reallocates as it goes. Do not change it.

Buffer counts its own copies and moves, and the harness reports both. There should be no copies at all. Right now there is a copy for every relocation, and one missing word is the reason.