What’s New in Latest Languages Features of C++23

Nitin Kumar
4 min readMar 25, 2022

--

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

--

--

Nitin Kumar

Working on ADAS as Technical Specialist. Blockchain enthusiast.