std::to_underlying(3)

πŸ“¦ version 2024.07.05, in nixpkgs unstable, opensuse-leap 16.0

πŸ’… std::to_underlying(3) β€” C++ Standard Library

Okay so bestie, we're talking about std::to_underlying, and honestly? It's giving "small function, huge glow-up." It lives in the C++ Standard Library and it does exactly one thing, but it does it flawlessly. πŸŽ€


✨ NAME ✨

std::to_underlying β€” yes she's literally named the same thing twice, that's her whole brand, no notes πŸ’…


πŸ¦‹ Synopsis πŸ¦‹

So here's the vibe. She's defined in the header <utility>, and the signature is:

template< class Enum >
constexpr std::underlying_type_t<Enum> to_underlying( Enum e )

(since C++23) β€” yes, she's a newer girl,所δ»₯ζˆ‘δ»¬ stan a modern icon πŸ‘‘

noexcept;

And what does she do? She converts an enumeration to its underlying type. That's it. That's the whole thing. She's equivalent to:

return static_cast<std::underlying_type_t<Enum>>(e);

No cap, that's literally the entire implementation and she still ate. πŸ”₯


🌸 Parameters 🌸

  • e β€” the enumeration value you want to convert. hand her the enum, she hands you back the int vibes 🫢

⭐ Return value ⭐

The integer value of the underlying type of Enum, converted from e. So like, if Enum is backed by char, you get a char. If it's backed by std::uint32_t, you get a std::uint32_t. Match made in heaven, no awkward casting situations. πŸ’–


🧚 Notes 🧚

Here's the actual tea, and it's important: std::to_underlying can be used to avoid converting an enumeration to an integer type other than its underlying type. Like, that's the whole point. Don't just cast to int and hope for the best β€” she keeps you honest. πŸ™ˆ

And there's a feature-test macro for her, because of course there is:

  • Feature-test macro: __cpp_lib_to_underlying
  • Value: 202102L
  • Std: (C++23)
  • Feature: std::to_underlying

πŸ’œ Example πŸ’œ

// Run this code

#include <cstdint>
#include <iomanip>
#include <iostream>
#include <type_traits>
#include <utility>

enum class E1 : char { e };
static_assert(std::is_same_v<char, decltype(std::to_underlying(E1::e))>);

enum struct E2 : long { e };
static_assert(std::is_same_v<long, decltype(std::to_underlying(E2::e))>);

enum E3 : unsigned { e };
static_assert(std::is_same_v<unsigned, decltype(std::to_underlying(e))>);

int main()
{
    enum class ColorMask : std::uint32_t
    {
        red = 0xFF, green = (red << 8), blue = (green << 8), alpha = (blue << 8)
    };

    std::cout << std::hex << std::uppercase << std::setfill('0')
              << std::setw(8) << std::to_underlying(ColorMask::red) << '\n'
              << std::setw(8) << std::to_underlying(ColorMask::green) << '\n'
              << std::setw(8) << std::to_underlying(ColorMask::blue) << '\n'
              << std::setw(8) << std::to_underlying(ColorMask::alpha) << '\n';

//  std::underlying_type_t<ColorMask> x = ColorMask::alpha; // Error: no known conversion
    [[maybe_unused]]
    std::underlying_type_t<ColorMask> y = std::to_underlying(ColorMask::alpha); // OK
}

🌈 Output:

000000FF
0000FF00
00FF0000
FF000000

Look at those three static_asserts at the top β€” she proves her type game at compile time, main character energy fr fr. And the color mask bit? Chef's kiss. The direct assignment is a whole error (no known conversion 😭) but through std::to_underlying? Totally OK. She's the friend who makes the impossible conversion happen. 🫢


πŸ‘‘ See also πŸ‘‘

  • underlying_type β€” obtains the underlying integer type for a given enumeration type (C++11) (class template)
  • is_enum β€” checks if a type is an enumeration type (C++11) (class template)
  • is_scoped_enum β€” checks if a type is a scoped enumeration type (C++23) (class template)

πŸ’€ tiny reminder bestie: this whole page is (since C++23) energy β€” if you're compiling with something older she simply does not exist yet. Check your standard, iykyk.

That's the whole page, mwah πŸ’‹ β€” sourced from cppreference.com, version 2024.06.10. Go convert some enums, you're gonna slay. βœ¨πŸŽ€πŸ¦‹

πŸ“¦ where this page comes from