std::byteswap is a constexpr, noexcept function template in <bit> introduced in C++23 (via proposal P1272R4) that reverses the byte order of an integer value. Designed for high-throughput network packet serialization, cross-platform binary file I/O, and hardware register configuration, std::byteswap maps directly to hardware CPU instructions (such as BSWAP or MOVBE on x86-64, or REV on ARM). It permanently replaces non-portable C socket macros (htonl, ntohl), vendor-specific intrinsics (__builtin_bswap32, _byteswap_ulong), and fragile bit-shift loops with a type-safe, constexpr-capable C++ primitive.
Prior to C++23, converting binary numbers between Host Byte Order (Little-Endian on x86/ARM) and Network Byte Order (Big-Endian) required using C POSIX socket functions or compiler-specific built-in instructions:
// Pre-C++23 Non-Portable Endian Byte Swapping
#if defined(_WIN32)
uint32_t net_val = _byteswap_ulong(host_val); // MSVC Intrinsic
#elif defined(__GNUC__) || defined(__clang__)
uint32_t net_val = __builtin_bswap32(host_val); // GCC/Clang Intrinsic
#else
uint32_t net_val = htonl(host_val); // POSIX C API (Requires <arpa/inet.h> or <winsock2.h>)
#endif- Missing 64-Bit & Width-Agnostic Support: POSIX
htonlandntohlsupported 16-bit (short) and 32-bit (long) integers only. Swapping 64-bit integers (std::uint64_t),std::size_t, or custom integer types required writing extra macro branches or custom shift loops. - Inability to Execute in
constexpr: C POSIX functions (htonl) and compiler built-in functions were non-constexpr. They could not be evaluated at compile time insidestatic_assertorconstexprlookup tables. - Fragile Shift/Mask Loops: Writing manual shift loops (
(x >> 24) | ((x & 0xFF0000) >> 8) | ...) routinely introduced sign-extension bugs when operating on signed integer types. Additionally, in un-optimized debug builds (-O0), shift loops generated dozens of slow instructions instead of emitting a single CPUBSWAPinstruction.
Solution: ISO C++23 introduced std::byteswap in <bit>, providing a type-safe, constexpr function template for all integral types.
-
Type Constraints:
$T$ must be an integral type (std::is_integral_v<T> == true), excludingbool. -
Underlying Byte Reversal Protocol: Reverses the exact sequence of
$N = \text{sizeof}(T)$ object bytes in memory:
Type Size (sizeof(T)) |
Sample Types | Execution Behavior | Assembly Mapping (x86_64 / ARM) |
|---|---|---|---|
| 1 Byte | std::uint8_t, char, char8_t |
No-Op: Returns value unmodified. |
MOV (0 instruction overhead) |
| 2 Bytes | std::uint16_t, char16_t |
Swaps byte 0 and byte 1. | ROL ax, 8 / REV16 |
| 4 Bytes | std::uint32_t, int, char32_t |
Reverses 4 constituent bytes. | BSWAP eax / REV |
| 8 Bytes | std::uint64_t, std::size_t |
Reverses 8 constituent bytes. | BSWAP rax / REV |
┌──────────────────────────────────────────────────────────────────┐
│ C++20 │
├──────────────────────────────────────────────────────────────────┤
│ • Introduced <bit> header with std::bit_cast and std::endian │
│ • Byte swapping function still missing from standard │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ C++23 │
├──────────────────────────────────────────────────────────────────┤
│ • Introduced std::byteswap in <bit> (P1272R4) │
│ • Full constexpr and noexcept support across all integral types │
│ • Replaced POSIX htonl/ntohl and vendor built-ins │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ C++26 │
├──────────────────────────────────────────────────────────────────┤
│ • Native integration with C++26 SIMD Parallel Types (std::simd) │
└──────────────────────────────────────────────────────────────────┘
Demonstrates converting multi-word protocol header integers between Host Byte Order and Big-Endian Network Byte Order.
#include <iostream>
#include <bit>
#include <cstdint>
struct NetworkPacketHeader {
std::uint16_t magic_id;
std::uint32_t sequence_num;
std::uint64_t payload_checksum;
};
// Generic Endian Conversion Helper: Converts Big-Endian Network data to Host Byte Order
template <typename T>
constexpr T network_to_host(T net_val) noexcept {
// If native platform is Little-Endian (x86/ARM), swap bytes!
if constexpr (std::endian::native == std::endian::little) {
return std::byteswap(net_val);
}
return net_val; // Already Big-Endian
}
int main() {
// Incoming Big-Endian Network Packet Payload
NetworkPacketHeader raw_net_packet{
.magic_id = 0x1234,
.sequence_num = 0x0000'0042,
.payload_checksum = 0x1122'3344'5566'7788ULL
};
// Convert packet fields to Host Byte Order
NetworkPacketHeader host_packet{
.magic_id = network_to_host(raw_net_packet.magic_id),
.sequence_num = network_to_host(raw_net_packet.sequence_num),
.payload_checksum = network_to_host(raw_net_packet.payload_checksum)
};
std::cout << "Raw Net Checksum: 0x" << std::hex << raw_net_packet.payload_checksum << "\n";
std::cout << "Host Checksum: 0x" << std::hex << host_packet.payload_checksum
<< " (Expected: 0x8877665544332211)\n";
return 0;
}Demonstrates performing byte swapping inside compile-time static_assert statements.
#include <iostream>
#include <bit>
#include <cstdint>
constexpr bool verify_constexpr_byteswap() {
std::uint32_t val32 = 0x11223344u;
std::uint64_t val64 = 0x1122334455667788ULL;
// C++23 std::byteswap is fully constexpr-capable!
bool check32 = (std::byteswap(val32) == 0x44332211u);
bool check64 = (std::byteswap(val64) == 0x8877665544332211ULL);
return check32 && check64;
}
int main() {
// Verified entirely at COMPILE TIME!
static_assert(verify_constexpr_byteswap(), "Compile-time byteswap verification failed");
std::cout << "Compile-time std::byteswap verified successfully.\n";
return 0;
}Demonstrates how to swap bytes of non-integral floating-point types (float, double) safely by pairing std::bit_cast with std::byteswap.
#include <iostream>
#include <bit>
#include <cstdint>
constexpr float byteswap_float(float f) noexcept {
// 1. Bit-cast float -> uint32_t (Aliasing-safe!)
auto bits = std::bit_cast<std::uint32_t>(f);
// 2. Swap bytes using std::byteswap
auto swapped_bits = std::byteswap(bits);
// 3. Bit-cast uint32_t -> float
return std::bit_cast<float>(swapped_bits);
}
int main() {
constexpr float original = 3.14159f;
constexpr float swapped = byteswap_float(original);
constexpr float restored = byteswap_float(swapped);
static_assert(original == restored, "Round-trip float byteswap failed");
std::cout << "Original Float: " << original << "\n";
std::cout << "Restored Float: " << restored << " (Exact match!)\n";
return 0;
}-
Use
std::byteswapfor All Binary Protocol Endian Conversions:- Replace legacy POSIX
htonl/ntohlmacros, MSVC_byteswap_*built-ins, and GCC__builtin_bswap*calls with standardstd::byteswap.
- Replace legacy POSIX
-
std::byteswapAccepts Integral Non-Bool Types ONLY:- Calling
std::byteswapon floating-point numbers (float,double) orboolcauses compilation errors. - Fix: To swap bytes on a
floatordouble, bit-cast the value to an unsigned integer type first usingstd::bit_cast:std::bit_cast<float>(std::byteswap(std::bit_cast<uint32_t>(f))).
- Calling
-
Condition Byte Swapping on System Endianness (
std::endian):- Combine
std::byteswapwithif constexpr (std::endian::native == std::endian::little)to execute byte swapping strictly when the host platform architecture requires endian conversion.
- Combine
- Related to:
- Bit Operations () — Base hardware bit manipulation header.
- std::bit_cast — Type-safe bit reinterpretation utility used to swap non-integral types.
std::endian— System endianness inspection enum class.
- Superseded by: None.
- Supersedes: POSIX C socket macros (
htonl,ntohl,htons,ntohs), vendor intrinsics (__builtin_bswap32), and manual bitwise shift loops.
Back to C++23 section.