Skip to content

77 Advanced The Hundred

The Maximum That Vanished

saving the winner is what loses it

#include <algorithm>
#include <iostream>

int main() {
    int score = 10;
    int bonus = 32;

    const int& best = std::max(score, score + bonus);   // no need to copy an int

    std::cout << "inline: " << std::max(score, score + bonus) << "\n";
    std::cout << "saved:  " << best << "\n";
}

Build it with -O2 and run it. Do the two lines agree?

Answer

Typical output (GCC 11 on x86-64, -O2): inline: 42, then saved: 0. The best score evaporated between one line and the next.

Why

std::max does not hand back an int — it returns const T&, a reference to whichever argument won, and here the winner is the temporary score + bonus. That temporary dies at the semicolon, so best refers to a dead int and reading it is undefined behavior; the inline line is safe only because its temporary is still alive inside the same statement. Binding a reference to a temporary normally extends the temporary's lifetime, but only when the binding is direct — a reference handed back by a function earns no extension, which is why std::min, std::minmax and std::clamp share the hazard.

It may even appear to work: GCC 11 and 13 recycle the dead slot from -O1 up but print 42 twice at -O0, while Clang 18 prints 42 twice at every -O level. GCC 13's -Wall flags the binding itself (possibly dangling reference to a temporary [-Wdangling-reference]), Clang 18 stays silent, and -fsanitize=address reports stack-use-after-scope on both.

The fix

Take the winner by value — it is an int, the copy costs nothing:

int best = std::max(score, score + bonus);

Takeaway: an algorithm that returns a reference returns it into one of your arguments, temporaries included — store the result by value.

Try it: g++ -std=c++17 -O2 main.cpp -o demo && ./demo

Open in Compiler Explorer ↗ Quiz this entry