Skip to content
C++
Library
since C++98
Intermediate

<utility>

The <utility> header provides foundational C++ building blocks — move, forward, swap, exchange, pair, declval, and safe integer comparisons.

<utility>since C++98

A 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:

AdditionStandard
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_sequenceC++14
std::as_const, std::in_place* tagsC++17
std::cmp_*, std::in_rangeC++20
std::to_underlying, std::forward_like, std::unreachableC++23

Syntax

cpp
// 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.

cpp
#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 copies

std::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.

cpp
#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:

cpp
#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.

cpp
#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++11

std::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.

cpp
#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.

cpp
#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:

cpp
#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:

cpp
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.

See Also

  • reference/idioms/value-semantics — ownership and move semantics design patterns
  • reference/language/decltypedecltype specifier used alongside std::declval
  • <tuple> — generalisation of std::pair to N elements
  • <type_traits> — type predicates used with <utility> utilities