<utility>
The <utility> header provides foundational C++ building blocks — move, forward, swap, exchange, pair, declval, and safe integer comparisons.
<utility>since C++98A standard library header providing general-purpose primitives for move semantics, perfect forwarding, object exchange, pairs, and — since C++20 — safe integer comparison functions.
Overview
<utility> is the catch-all header for small but foundational building blocks that don't belong to any single data-structure or algorithm category. Its surface area has grown with every standard:
| Addition | Standard |
|---|---|
std::pair, std::swap (copy-based) | C++98 |
std::move, std::forward, std::move_if_noexcept, std::declval, std::swap (move-based) | C++11 |
std::exchange, std::integer_sequence | C++14 |
std::as_const, std::in_place* tags | C++17 |
std::cmp_*, std::in_range | C++20 |
std::to_underlying, std::forward_like, std::unreachable | C++23 |
Syntax
// Move semantics primitives (C++11)
template<typename T> constexpr std::remove_reference_t<T>&& move(T&&) noexcept;
template<typename T> constexpr T&& forward(std::remove_reference_t<T>&) noexcept;
template<typename T> constexpr T&& move_if_noexcept(T&) noexcept;
// Swap and exchange (C++98 / C++14)
template<typename T> void swap(T&, T&) noexcept(/*see below*/); // C++98, move-based since C++11
template<typename T, typename U = T> T exchange(T&, U&&); // C++14
// Pair (C++98)
template<typename T1, typename T2> struct pair;
template<typename T1, typename T2> constexpr pair<T1,T2> make_pair(T1&&, T2&&);
// Type utilities
template<typename T> constexpr std::add_const_t<T>& as_const(T&) noexcept; // C++17
template<typename T> typename std::add_rvalue_reference<T>::type declval() noexcept; // C++11, unevaluated only
// Safe integer comparison (C++20)
template<typename T, typename U> constexpr bool cmp_equal(T, U) noexcept;
template<typename T, typename U> constexpr bool cmp_less(T, U) noexcept;
template<typename T> constexpr bool in_range(std::intmax_t) noexcept;
// Enum utility (C++23)
template<typename Enum> constexpr std::underlying_type_t<Enum> to_underlying(Enum) noexcept;Examples
Move semantics and perfect forwarding
std::move does not move anything — it produces an rvalue reference, enabling move constructors and move assignment operators to be selected by overload resolution. std::forward preserves the value category of a forwarding reference, enabling perfect forwarding in templates.
#include <utility>
#include <vector>
#include <string>
class Buffer {
std::vector<std::byte> data_;
public:
Buffer(Buffer&& other) noexcept
: data_(std::move(other.data_)) // C++11: cast to rvalue, triggers vector move ctor
{}
Buffer& operator=(Buffer&& other) noexcept {
data_ = std::move(other.data_);
return *this;
}
};
// Perfect forwarding factory (C++11)
template<typename T, typename... Args>
T create(Args&&... args) {
return T(std::forward<Args>(args)...);
}
auto s = create<std::string>(5, 'x'); // forwards (size_t, char) — no copiesstd::exchange in move constructors
std::exchange (C++14) replaces a value and returns the old one. It eliminates a common pattern in move constructors where you null out the source and need the original value.
#include <utility>
class Handle {
int fd_ = -1;
public:
Handle(int fd) : fd_(fd) {}
Handle(Handle&& other) noexcept
: fd_(std::exchange(other.fd_, -1)) // C++14: take fd_, set other.fd_ to -1 atomically
{}
Handle& operator=(Handle&& other) noexcept {
if (this != &other) {
close_if_open();
fd_ = std::exchange(other.fd_, -1);
}
return *this;
}
~Handle() { close_if_open(); }
private:
void close_if_open() { if (fd_ != -1) ::close(fd_); }
};The swap customisation point
std::swap is a customisation point. The correct way to write a swap function for your type is as a non-member in the same namespace, not as a specialisation of std::swap. Call it with the using-declaration pattern to allow ADL to find your overload first, then fall back to the generic template:
#include <utility>
namespace geo {
struct Point { double x, y; };
void swap(Point& a, Point& b) noexcept {
using std::swap; // ADL fallback for members that don't have their own swap
swap(a.x, b.x);
swap(a.y, b.y);
}
} // namespace geo
// At the call site, always invoke swap with the using pattern:
void sort_two(geo::Point& a, geo::Point& b) {
using std::swap;
if (b.x < a.x) swap(a, b); // finds geo::swap via ADL
}std::pair
std::pair<T1, T2> stores two heterogeneous values in first and second. It is comparable, copyable, movable, and swappable. Since C++17 it participates in structured bindings.
#include <utility>
#include <map>
#include <string>
std::map<std::string, int> word_count;
auto [it, inserted] = word_count.emplace("hello", 1); // C++17 structured binding on pair
// make_pair deduces types; direct construction is often cleaner in C++11+
std::pair<std::string, int> entry{"world", 42};
// std::tie extracts into existing variables (C++11); prefer structured bindings in C++17+
std::string key;
int value;
std::tie(key, value) = entry; // C++11std::declval — unevaluated context
std::declval<T>() produces an rvalue reference to T without requiring a constructor. It is only valid in unevaluated operands (decltype, sizeof, noexcept, requires). Use it to query expression types for types that aren't default-constructible.
#include <utility>
#include <type_traits>
template<typename Container>
using element_type = decltype(*std::declval<Container>().begin());
static_assert(std::is_same_v<element_type<std::vector<int>>, int&>);
// SFINAE: detect whether T + U is well-formed (C++11/14 style)
template<typename T, typename U, typename = void>
struct can_add : std::false_type {};
template<typename T, typename U>
struct can_add<T, U, std::void_t<decltype(std::declval<T>() + std::declval<U>())>>
: std::true_type {};Safe integer comparison (C++20)
Comparing signed and unsigned integers directly triggers undefined behaviour or produces surprising results due to implicit conversions. The std::cmp_* family performs mathematically correct comparisons regardless of signedness.
#include <utility>
#include <cstddef>
void check_index(int idx, std::vector<int>& v) {
// Dangerous: if idx < 0, the comparison is UB or silently wrong
// if (idx < v.size()) { ... }
// Correct (C++20):
if (std::cmp_greater_equal(idx, 0) && std::cmp_less(idx, v.size())) {
v[idx] = 0;
}
// std::in_range checks whether a value fits in the target type (C++20)
static_assert(std::in_range<unsigned char>(255));
static_assert(!std::in_range<unsigned char>(256));
}std::to_underlying (C++23)
Converts a scoped or unscoped enum to its underlying integer type without static_cast boilerplate:
#include <utility>
#include <cstdint>
enum class Color : uint8_t { Red = 1, Green = 2, Blue = 4 };
void set_flags(uint8_t& reg, Color c) {
reg |= std::to_underlying(c); // C++23 — cleaner than static_cast<uint8_t>(c)
}Prior to C++23 the equivalent was static_cast<std::underlying_type_t<Color>>(c).
Best Practices
Prefer std::exchange over manual null-and-take in move operations. It makes the intent clear, is atomic in the sense that it can't leave the member in an intermediate state across two statements, and works as a one-liner.
Always use the using std::swap; + unqualified swap() pattern at every call site that swaps user-defined types. Calling std::swap directly defeats ADL and bypasses your type's optimised overload.
Use std::cmp_* whenever mixing signed and unsigned. The compiler warning -Wsign-compare fires on direct comparison, but the code may still compile. cmp_less and friends make the safety guarantee explicit in the type system.
Annotate move operations noexcept where possible. std::vector and other containers check std::is_nothrow_move_constructible to decide whether to move or copy during reallocation. Unannotated move constructors force copies.
Common Pitfalls
std::move on a named rvalue reference still needs std::move. Inside a function that takes T&&, the parameter is an lvalue. Forwarding it without std::move copies:
void bad(std::string&& s) {
consume(s); // copies — s is an lvalue here
}
void good(std::string&& s) {
consume(std::move(s)); // moves
}std::forward outside a deduced forwarding reference context is wrong. Calling std::forward<T> where T is not a deduced template parameter produces incorrect value categories. It is only meaningful inside a template where T&& was deduced.
std::pair::first and second are public data members, not functions. Calling .first() compiles only if a callable is stored — otherwise it is a silent type error or a hard compile failure depending on context.
std::declval in an evaluated context is ODR-ill-formed. It has no definition; using it outside decltype or a requires-expression will cause a linker error, not a compile error.
std::exchange
std::exchange(obj, new_val) atomically replaces obj with new_val and returns the old value. This makes it the canonical tool for implementing move constructors and move assignment operators — the source is zeroed out in a single expression rather than two separate statements.
#include <utility>
class Resource {
void* ptr_ = nullptr;
public:
// C++14: move constructor using std::exchange
Resource(Resource&& other) noexcept
: ptr_(std::exchange(other.ptr_, nullptr)) // C++14
{}
Resource& operator=(Resource&& other) noexcept {
if (this != &other) {
release();
ptr_ = std::exchange(other.ptr_, nullptr); // C++14
}
return *this;
}
~Resource() { release(); }
private:
void release() { if (ptr_) { free(ptr_); ptr_ = nullptr; } }
};The manual equivalent requires two statements and risks leaving the object in an intermediate state if an exception fires between them. std::exchange is also useful in state-machine resets where you need the previous value before installing a new one.
#include <utility>
struct Parser {
int result_ = 0;
// Reset result_ to Default{} and return the accumulated value // C++14
int flush() {
return std::exchange(result_, 0); // C++14
}
};
// Compare to manual swap — std::exchange is more expressive when old value is needed
// old = x; x = new_val; → old = std::exchange(x, new_val); // C++14std::as_const and std::move_if_noexcept
std::as_const(x) returns a const T& to x, forcing the compiler to select the const overload without a cast. This is especially valuable in generic code where you want to verify the const path compiles, or where implicit conversion might otherwise pick the non-const overload.
#include <utility>
#include <type_traits>
struct Logger {
void log() { /* non-const: logs with side-effect counter */ }
void log() const { /* const: read-only snapshot log */ }
};
void use(Logger& lg) {
lg.log(); // non-const overload
std::as_const(lg).log(); // const overload — no cast needed // C++17
}std::move_if_noexcept(x) produces an rvalue reference only when the move constructor is noexcept; otherwise it produces an lvalue reference, triggering a copy. This is exactly the policy std::vector uses during reallocation to preserve the strong exception guarantee: if moving can throw, it copies to ensure rollback is possible.
#include <utility>
#include <type_traits>
struct SafeValue {
SafeValue(SafeValue&&) noexcept = default; // noexcept move — will be moved
};
struct UnsafeValue {
UnsafeValue(UnsafeValue&&) {} // may throw — will be copied
};
static_assert(std::is_nothrow_move_constructible_v<SafeValue>); // C++17
static_assert(!std::is_nothrow_move_constructible_v<UnsafeValue>); // C++17
void reallocate(SafeValue& s, UnsafeValue& u) {
auto&& a = std::move_if_noexcept(s); // C++17: rvalue ref — move will be selected
auto&& b = std::move_if_noexcept(u); // C++17: lvalue ref — copy will be selected
}std::to_underlying (C++23)
std::to_underlying(e) is the clean, intent-revealing way to extract the underlying integer from a scoped enum. Prior to C++23 this required static_cast<std::underlying_type_t<Color>>(e) — verbose enough that developers sometimes used reinterpret_cast or C-style casts by mistake.
#include <utility>
#include <cstdint>
enum class Color : uint8_t { Red = 1, Green = 2, Blue = 4 };
// Pre-C++23: verbose and error-prone
uint8_t old_way = static_cast<std::underlying_type_t<Color>>(Color::Red);
// C++23: clear and concise
uint8_t new_way = std::to_underlying(Color::Red); // C++23Serialization is a common use case where enum values must be written as raw bytes:
#include <utility>
#include <ostream>
#include <cstdint>
#include <format>
enum class Status : uint16_t { Ok = 200, NotFound = 404, Error = 500 };
void serialize(std::ostream& os, Status s) {
uint16_t raw = std::to_underlying(s); // C++23: write integer, not enum token
os.write(reinterpret_cast<const char*>(&raw), sizeof(raw));
}
// std::format cannot print scoped enums directly; std::to_underlying bridges the gap
std::string describe(Status s) {
return std::format("HTTP status: {}", std::to_underlying(s)); // C++23
}std::pair and std::tuple Operations
std::pair<T1, T2> is the simplest heterogeneous aggregate in the standard library, and its API has been progressively enhanced to align with modern C++ idioms. Prefer std::pair{a, b} (CTAD, C++17) over std::make_pair(a, b) in new code — CTAD is shorter and avoids an extra function call.
#include <utility>
#include <string>
// C++11: make_pair deduces types
auto p1 = std::make_pair(std::string{"key"}, 42); // C++11
// C++17: CTAD — same result, less boilerplate
std::pair p2{std::string{"key"}, 42}; // C++17Type-based std::get (C++14) avoids error-prone numeric indices when one of the types is unique:
#include <utility>
#include <tuple>
#include <string>
std::pair<std::string, int> entry{"hello", 7};
auto& name = std::get<std::string>(entry); // C++14: type-based access
auto count = std::get<1>(entry); // C++11: index-based access
// std::tie unpacks into existing variables (C++11)
std::string k; int v;
std::tie(k, v) = entry; // C++11
// Structured bindings are the preferred modern alternative (C++17)
auto [key, val] = entry; // C++17Pairs compare lexicographically, and std::tuple_cat merges two or more tuples (including pairs) into a single tuple:
#include <utility>
#include <tuple>
std::pair<int,int> a{1, 2}, b{1, 3};
static_assert(a < b); // C++11: lexicographic comparison
// Merge pairs or tuples into a single tuple (C++11)
auto merged = std::tuple_cat(a, b); // C++11: tuple<int,int,int,int>{1,2,1,3}
auto [x1, y1, x2, y2] = merged; // C++17: unpack merged tuplestd::integer_sequence and Compile-Time Index Generation
std::integer_sequence<T, Ns...> and its convenience alias std::index_sequence<Ns...> carry a compile-time list of integers as a type, enabling pack expansion over indices. The matching factory std::make_index_sequence<N> generates index_sequence<0, 1, ..., N-1> without manual template recursion.
#include <utility>
#include <tuple>
#include <type_traits>
// C++14: index_sequence as a tag type used in a helper overload
template<typename F, typename Tuple, std::size_t... Is>
auto apply_impl(F&& f, Tuple&& t, std::index_sequence<Is...>) // C++14
-> decltype(std::forward<F>(f)(std::get<Is>(std::forward<Tuple>(t))...))
{
return std::forward<F>(f)(std::get<Is>(std::forward<Tuple>(t))...);
}
template<typename F, typename Tuple>
auto apply(F&& f, Tuple&& t) // C++14
{
constexpr auto N = std::tuple_size<std::remove_reference_t<Tuple>>::value;
return apply_impl(std::forward<F>(f),
std::forward<Tuple>(t),
std::make_index_sequence<N>{}); // C++14
}
// Note: std::apply is in <tuple> since C++17; this example shows the technique.The index-sequence idiom enables compile-time dispatch that collapses to a single function call at runtime with zero overhead:
#include <utility>
#include <array>
// C++14: expand array elements as individual arguments
template<typename F, std::size_t N, std::size_t... Is>
void foreach_impl(F&& f, const std::array<int, N>& arr,
std::index_sequence<Is...>) // C++14
{
(f(arr[Is]), ...); // C++17 fold — unrolled at compile time
}
template<typename F, std::size_t N>
void foreach_array(F&& f, const std::array<int, N>& arr) {
foreach_impl(std::forward<F>(f), arr,
std::make_index_sequence<N>{}); // C++14
}std::unreachable and std::assume (C++23)
std::unreachable() marks a code path the programmer guarantees will never be executed; reaching it is undefined behaviour, which the compiler exploits for optimisation and dead-code elimination. It is the portable replacement for GCC/Clang's __builtin_unreachable() and MSVC's __assume(false).
#include <utility> // std::unreachable (C++23)
#include <cassert>
enum class Direction { North, South, East, West };
int delta_x(Direction d) {
switch (d) {
case Direction::North: return 0;
case Direction::South: return 0;
case Direction::East: return 1;
case Direction::West: return -1;
default: std::unreachable(); // C++23: silences -Wreturn-type, enables optimisation
}
}[[assume(cond)]] (C++23) tells the compiler that cond is always true at that point — without emitting a runtime check. This differs from assert() (which is a runtime check, stripped in release builds) and from std::unreachable() (which marks a path, not a condition).
#include <utility>
// C++23: [[assume]] vs assert() vs std::unreachable()
void process(int* ptr, std::size_t n) {
[[assume(ptr != nullptr)]]; // C++23: no branch generated; optimizer may hoist loads
[[assume(n > 0)]]; // C++23: loop can skip empty-range guard
for (std::size_t i = 0; i < n; ++i)
ptr[i] *= 2;
}
// assert() fires at runtime in debug builds, is removed in release — not a hint to the optimizer
// __builtin_unreachable() is GCC/Clang only; std::unreachable() is the portable C++23 equivalentSee Also
reference/idioms/value-semantics— ownership and move semantics design patternsreference/language/decltype—decltypespecifier used alongsidestd::declval<tuple>— generalisation ofstd::pairto N elements<type_traits>— type predicates used with<utility>utilities