February's C++23 plenary adopts smaller interface repairs
The virtual plenary adds to_underlying and other targeted improvements to the C++23 draft. Enum conversion shows how a small utility can remove a maintenance hazard at a typed interface boundary.
An explicit cast can preserve an obsolete assumption through a refactoring. Changing an enumeration's underlying type does not automatically change a cast written to int years earlier. The compiler sees an intentional conversion, while the programmer may have intended only to extract the enumeration's actual representation. A small library utility can express that distinction directly.
The February 22 virtual plenary report records adoption of std::to_underlying into the C++23 working draft. Herb Sutter also reports targeted language and ranges improvements. This is draft-standard progress, not a claim that every installed C++ library already supplies the new facility.
I value the parts of C++ that let an abstraction compile into a direct operation without forcing callers to repeat its type machinery. to_underlying fits that preference. Its implementation is small, but its name distinguishes extracting a representation from converting an enum to an arbitrarily selected integer type.
The argument supplies the type
JeanHeyd Meneide's P1682R3 specifies the utility in <utility>. The function returns underlying_type_t<T> for an enumeration argument of type T, using the corresponding static_cast. It is constexpr and noexcept. An input that is not an enumeration is outside the intended interface.
The difference from static_cast<int>(value) is the source of the target type. An explicit cast embeds the caller's choice of int. The utility derives the result type from the argument. If the enum later uses another underlying integer type, the extraction follows it without requiring a matching edit at every call site.
A longer cast using std::underlying_type_t<SomeEnum> can express the same conversion for the current declaration. It still repeats the enum's name. If the parameter changes to another enum while the old type remains declared, that repetition can survive and become wrong. Deducing the type from the value removes that particular opportunity for disagreement.
This is especially relevant at an interface that accepts an integer representation. A C API or an internal untyped operation may require such a value. Keeping the enum strongly typed until that boundary remains useful, but the final conversion should describe what the boundary actually needs. to_underlying names that extraction point.
The proposal also notes the value of a recognizable spelling when searching a codebase. A search for the utility locates intentional conversions out of enumeration types. Searching every static_cast instead mixes these operations with many unrelated conversions. This does not replace type checking, but it makes a particular class of interface boundary easier to locate during maintenance and review.
Extraction does not validate the receiving API
The utility does not establish that the receiving API accepts every value of the enum's underlying type. A later implicit conversion may still occur if the API takes a different integer type. The proposal argues that moving the explicit cast into the utility lets ordinary warnings identify some size and signedness mismatches at the subsequent use.
That is a narrower and more useful claim than calling the conversion universally safe. Extracting a representation and passing it through a particular interface are separate operations. A call can use the correct underlying type and still violate a protocol's permitted numeric range. The utility removes duplicated type selection, not every possible interpretation error.
For illustration, draft-supporting code can express extraction directly:
#include <utility>
enum class mode : unsigned { read = 1, write = 2 };
constexpr auto representation = std::to_underlying(mode::write);
static_assert(representation == 2u);
This example depends on the newly adopted library facility. The proposal also specifies a feature-test macro, __cpp_lib_to_underlying, for detecting support. A draft adoption and an implementation's availability are different facts, so a library targeting existing toolchains needs to establish the latter through its supported environments.
The plenary also repairs existing inconsistencies
Sutter's report describes the meeting as the second pandemic-era full-committee plenary and records participation from eighteen voting nations. Work proceeds through subgroup meetings before plenary adoption. The resulting set includes small changes that complete or regularize existing features rather than a single large language addition.
One adopted language cleanup permits omitting an empty lambda parameter list when mutable is present. The report also identifies ranges corrections concerning iterator categories and borrowed ranges. These examples explain the meeting's character. Existing facilities can need integration work before their combinations behave as users expect.
I consider that integration work valuable in a language whose codebases contain several generations of idioms. A small inconsistency in a common operation can appear throughout a large program. Fixing its rule can reduce incidental complexity without asking the program to adopt an unrelated new abstraction.
The report separately says that the proposed constexpr additions to shared C mathematical functions did not reach consensus at this meeting. Concerns included implementation questions around errno in constant evaluation. That distinction is important to the meeting's record. General support for a direction does not equal adoption of its wording, and interfaces shared with C bring constraints beyond the C++ library implementation alone.