deniz.in

Markets

Weather

Loading weather

· via Hacker News – Front Page (native)

C++23 widens implicit move rules so std::move becomes needed even less often

A post by Andreas Fertig, now on the Hacker News front page, explains how copy elision and C++23's wider implicit-move rules let C++ returns avoid both copies and explicit std::move.

C++23 widens implicit move rules so std::move becomes needed even less often

A blog post by Andreas Fertig, published on 1 September 2026 and picked up on the Hacker News front page, argues that explicit std::move casts are rarely necessary in modern C++ — and that C++23 closes the remaining gaps where you still needed one.

The post, titled Move in C++ without a std::move, builds on Fertig's earlier advice that std::move should be used sparingly. He walks through what the compiler already does on its own, and where the language rules have only recently caught up.

Copy elision comes before moves

According to Fertig, unnecessary copies are among the costliest mistakes in this kind of code, and the strongest defense is not a move at all but copy elision. Under return value optimization (RVO), the returned object is constructed directly at the call site rather than on the function's stack, so neither a copy nor a move is performed. He also points out that the standard never frames this as an optimization; it specifies it as copy elision.

Since C++17, elision is guaranteed when a function returns a pure temporary:

cpp Apple RVO() { return {}; }

The named variant, NRVO, where a local such as Apple res{} is returned, is not covered by that guarantee, but Fertig notes that in practice you typically pay neither a copy nor a move there either.

The cases that lagged behind

Moving is the fallback when elision does not apply, and C++11 already made some returns move implicitly: returning a by-value parameter, as in Apple Fun(Apple val) { return val; }, moves from the parameter instead of copying.

Two other shapes were messier. The first is a function that takes an rvalue reference and returns the received object by value:

cpp Apple Cat(Apple&& val) { return val; }

Before C++20, Fertig writes, this compiled but produced a copy construction on a conforming compiler such as GCC, while Clang — which he calls less conforming here — already performed a move. In other words, the compiler bending the rules happened to deliver the better result.

The second case he singles out, by his description, is a function that returns an rvalue reference from an rvalue reference parameter. That code previously failed to compile unless you moved the return value manually, which went directly against his advice to avoid explicit casts.

What changes in C++23 mode

The headline takeaway concerns the compiler's language mode. Once switched to C++23, Fertig reports, both of the awkward cases perform an implicit move with no std::move anywhere in sight. The manual cast, which he says used to be required for the best result in both examples, can be dropped, extending the use-std::move-rarely rule to cover returns from rvalue reference parameters too.

Why it matters

For C++ developers the payoff is performance that arrives without extra code. Whether returning a string, a vector or a larger struct avoids an accidental deep copy can hinge on exactly these subtleties, and on a hot path the difference between a copy and a move can be significant. The post also documents a portability trap: until the rule change, identical source could move with one major compiler and copy with another, leaving behavior dependent on compiler leniency rather than the standard. Building in C++23 mode removes that variance and lets teams strip noisy casts out of return statements. The distilled guidance: return temporaries where possible so elision is guaranteed, trust implicit moves elsewhere, and keep std::move for the rare cases where the language does not already do the right thing.

  • #c-plus-plus
  • #move-semantics
  • #compilers
  • #performance
  • #language-standards