What’s new in C++26 (part 2)

In a previous blog post, I talked about several features included in C++26: specifying a reason to delete a function, placeholder variables with no name, structured binding declaration as a condition, and user-generated static_assert messages. In this post, I will continue exploring the new features that have been already included in the C++26 standard.

#embed

#embed is a new preprocessor directive that works as #include but for binary data. Its motivation is pretty simple: developers often need to include resources in their application (encoded text, images, sound, video) etc. but there is no standard or common way to do so. In this context, a resource is any source of data that is accessible during compilation. The #embed directive is replaced with a comma-separated list of int literals.

constexpr unsigned char icon_resource[] =
{
#embed "app.ico"
};

constexpr std::vector<unsigned char> sound_resource
{
#embed "sound.wav"
};

There are several parameters that can be specified for #embed:

  • limit: defines an upper limit for the size of the resource to be embedded
  • prefix: adds the specified prefix to the embedded resource, but only if it is not empty
  • suffix: adds the specified suffix to the embedded resource, but only if it is not empty
  • if_empty: replaces the embedded resource with the specified data if the resource is empty

The following are two examples (the first taken from the proposal paper) for using these parameters:

constexpr unsigned char null_terminated_file_data[] = {
  #embed "might_be_empty.txt" \
    prefix(0xEF, 0xBB, 0xBF, ) /* UTF-8 BOM */ \
    suffix(,)
  0 // always null-terminated
};

constexpr unsigned char image[] = 
{
  #embed "brush.bmp" if_empty(0xBAADF00D)
};

You can read more about #embed here:

constexpr exceptions

Throwing exceptions in constant evaluated code has not been possible. This makes it hard in some cases to do error handling and report errors. C++26 relaxes constraints on constant expressions, allowing throw statements. Moreover, standard exception classes are now constexpr, so you can throw them from constant evaluated code.

Here is an example:

constexpr time parse_time(std::string_view input) {
   auto [correct, hh, mm, ss] = ctre::match<"([01]\d|2[0-3]):([0-5]\d):([0-5]\d)">(input);
	
   if (!correct) {
      throw std::exception("invalid time format");
   }

   return build_time(hh, mm, ss);
}

int main()
{
   try
   {
      constexpr time t1 = parse_time("12:30:59");  // [1]
      constexpr time t2 = parse_time("12:30:60");  // [2]
   }
   catch(std::exception const & e) {
   }
}

Previously, both calls at [1] and [2] would have resulted in a compile-time error due to the fact that throwing exceptions from constexpr functions was not possible. With these changes in C++26:

  • call at [1] is correct, no errors occur
  • call at [2] produces a compile-time error with the text “invalid time format”

It is important to note that even though throwing exceptions from constant evaluated code is now possible, it is mandatory to catch the possible thrown exceptions. Otherwise, a compile-time error is generated due to the uncaught exception.

constexpr time t1 = parse_time("12:30:59");  // compile time error because exceptions are uncaught

To learn more about this feature see:

Variadic friends

Class templates can grant access to their private and protected members to template parameter classes.

template<typename K, typename V>
class D {
  friend K;
  friend V;
};

However, doing the same for all classes in a parameter pack was not possible.

template <typename... T>
class D {
    friend T...;
};

C++26 makes the above snippet possible. Here is another example from the proposal paper, with nested classes:

template<class T>
struct C {
  template<class U> struct Nested;
};
 
template<class... Ts>
struct VS {
  template<class U>
  friend class C<Ts>::Nested...; // OK
};

Variadic friends can be useful in situations such as the CRTP with multiple base classes or granting access to individual member functions using the passkey idiom.

To learn more about this topic see:

You can check the support for this features with different compiler on cppreference: C++26.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.