What’s New in Latest Languages Features of C++23
1. Core language suffixes for size_t and its associated signed type
As a language feature, the design of the suffixes becomes much simpler. The core language only has one format for its integer literal suffixes: the letter(s), with an optional u
on either side of the letter(s) to make it unsigned, with the signed variant being the default on most architectures. We did not want to use s
because s
might mean short
to some and there are people working on the short float
paper currently wherein a suffix such as sf
might surface. In this case, it would make some small amount of sense for the suffix s
to also work for shorts, albeit that might have unforeseen consequences with standard-defined library literals.
The literal suffixes z
and uz
/zu
alongside t
and ut
/tu
were chosen to represent signed/unsigned size_t
and ptrdiff_t
, respectively. decltype(0t)
will yield ptrdiff_t
and decltype(0uz)
/decltype(0zu)
will yield size_t
. Like other case-insensitive language literal suffixes, it will accept both Z
/T
and z
/t
(and U
and u
alongside of it). This follows the current convention of the core language to be able to place u
and z
/t
in any order / any case for the suffix. For example,
Using t
for ptrdiff_t
and zu
for size_t
?
Previous invocations of this paper used only z
and uz
/zu
, mostly because there was no named type that represented what a signed std::size_t
or an unsigned std::ptrdiff_t
was. This made it awkward to place into the C++ wording for the author writing this paper. However, Core Wording experts (thanks Hubert Tong and Jens Maurer!) have helped elucidate that while the type may not have a formal name or type alias in the language, it is perfectly valid to say "the unsigned/signed integer type corresponding to {X}".
2. Removing unnecessary ( )’s from C++ lambdas.
Currently, C++ lambdas with no parameters do not require a parameter declaration clause.
If a lambda-expression does not include a lambda-declarator, it is as if the lambda-declarator were ( ).
This allows us to omit the unused ()
in simple lambdas such as this:
These particular lambdas have ownership of the strings, so they ought to be able to mutate it, but s1
and s2
are const (because the const
operator is declared const
by default) so we need to add the mutable
keyword:
This proposal would make these parentheses unnecessary like they were before we added mutable
. This will apply to:
- lambda template parameters
- constexpr
- mutable
- consteval
- Exception specifications and noexcept
- attributes
- trailing return types
- requires
3. Non-literal variables (and labels and gotos) in constexpr functions
This proposes to strike the restriction that a constexpr function cannot contain a definition of a variable of non-literal type (or of static or thread storage duration), or a goto statement, or an identifier label. The rationale is briefly that the mere presence of the aforementioned things in a function is not in and of itself problematic; we can allow them to be present, as long as constant evaluation doesn’t evaluate them.
According to tests on multiple compilers, there’s implementation divergence; some accept the testcase, some reject it, and for some it depends on exactly how the testcase function body is written. While some of that may be due to imperfections in implementations, the standard is inconsistent here; the wording is probably clear as such, but the declaration special cases that remain in the specification seem undesirable.
We have allowed a constexpr function to contain a throw-expression since C++11, as long as constant evaluation doesn’t evaluate it. We have since extended the set of allowed things to contain, for example, inline assembly. Removing the restriction of variable definitions seems perfectly in line with the general direction we’ve been going towards.
With the feature-testing macro, the work-around can be made inline, and removed from the actual code when not needed
4. Multidimensional subscript operator
User-defined types can define a subscript operator with multiple arguments to better support multi-dimensional containers and views.
Basically, what is mutli-dimensional array?
Multidimensional arrays map multiple integer indices to a reference to an element of the array. They naturally generalize single-dimensional arrays. Programmers use types that behave like multidimensional arrays to represent objects in many domains, including • matrices (as in linear algebra) and tensors; • discretized physical space (e.g., for physics simulations or video games); and • images (as in graphics). ex : a[x][y][z]: a chain of single-argument array access operators, as with C array-of-array
It is proposed that operator[] should be able to accept zero or more arguments, including variadic arguments. Both its use and definition would match that of operator(). We make the expressions deprecated in C++20 ill-formed while allowing multi-dimensional subscript expressions in new standard types and user types. We do not propose modifications to C arrays, so as to leave a cycle before giving new meaning to syntax that was still valid in C++20.
5. auto(x) and auto{x}
This proposes auto(x)
and auto{x}
for casting x
into a prvalue as if passing x
as a function argument by value. The functionality appears as the decay-copy
function in the standard for exposition only.
A generic way to obtain a copy of an object in C++ is auto a = x;
but such a copy is an lvalue. We could often convey the purpose in code more accurately if we can obtain the copy as a prvalue
The declaration’s primary purpose is to declare a variable, while the variable being a copy is the declaration’s property. In contrast, the expression to obtain an rvalue copy is a clear command to perform a copy:
auto(x.front())
#c++ #moderncpp #c++23 #language