My attempt to simplify C++

Since C++ started to move again with C++11 (no pun intended) we got a lot of new features and abilities in the language. That is great. Of course, not everything we want is already there. Some may never come. Evolving a language is hard. There is only limited time to get new features in, to test them, to improve them. But what I'm telling you, evolving a language is much like improving a software product. Not all feature fit into a release. Some of them are too last minute and may not 100% work as planned. Some may end up being used rarely to never by customers.

However, doing nothing is also not an option. C++ did it for a long enough time.

Like software a language sometimes needs fixes, for the same reasons. What to fix is equally hard to say. Different people have different needs and therefore see different priorities.

My attempt I like to share with you today is making the language a tiny bit simpler. Ironically often when we make things simpler that means we add another way of doing things and by that adding complexity. This is true for my attempt as well.

constexpr class

As I said I like to make one thing simpler and that is a class with only constexpr member functions. With the evolving C++ standards more and more constructs can be constexpr. C++20 added a whole new level to this plate with allowing dynamic memory allocations in constexpr context. We can have virtual constexpr member functions. Depending on the number of constexpr member functions we have to write constexpr all over the place. But for certain constructs the compiler is already able to add constexpr implicitly. Lambdas or compiler-provided constructors are and example.

With my proposal P2350R0 constexpr class I aim to simplify such a entirely constexpr class by instead of having to attribute each member function we can mark the class as constexpr. This reduces a lot of noise in the class declaration and carries an important message that is otherwise hard to see: this class consists of only constexpr member functions.

Let's see an example:

1
2
3
4
5
6
7
class SomeType {
public:
  constexpr bool empty() const { /* */ }
  constexpr auto size() const { /* */ }
  constexpr void clear() { /* */ }
  // ...
};

This is how some of our classes look these days. In terms of clean code and reducing repetitions constexpr does a very bad job. We have to write it three times, once for each member function.

With P2350R0 we achieve the same by writing this:

1
2
3
4
5
6
7
class SomeType constexpr {
public:
  bool empty() const { /* */ }
  auto size() const { /* */ }
  void clear() { /* */ }
  // ...
};

Instead of having to attribute each member function we declare the class itself, in the class-head, as constexpr. To me this feels much better than what we currently have to write.

XXXX class

One of the early feedbacks I got was, please don't stop at constexpr also think of other elements. While I listen to this feedback things are not that simple.

consteval class

Let's start with consteval. It falls right into the constexpr bucket and is a good candidate for getting added to the class-head like constexpr. But then we have little to no experience how consteval will be used. Is there really a need for classes with only consteval member functions? I don't know. From a standardization and implementation perspective it is like constexpr and by that doable. But adding something that is not used contradicts the simplifying the language goal.

noexcept class

Another candidate that was mentioned is noexcept. For sure there are classes out there where each and every member function is marked as noexcept. Yet, there is a difference to constexpr. One property of constexpr member functions is that they must be declared inline. They cannot be split between declaration and implementation. By that the implicitly constexpr declared members are right in the class with constexpr at its top. What is the right thing to do for noexcept function? Is it okay that they are declared implicitly noexcept and in a cpp-file we may not see it anymore?

There is an interesting difference to constexpr. If we violate the constexpr rules the compiler refuses to compiler our code. If we violate the noexcept guarantee we end up with out application terminating immediately at run-time once we reach that code. Compile-time error vs. run-time error. Whenever I can pick I pick the compile-time error. This makes me less fond of having a noexcept class.

virutal class

Especially for abstract classes we have a high chance of having virtual-only member functions. Sometimes if we derive from such a class we end up with only overridden members. A class with all members virtual would solve the ongoing issues with an unwanted non-virtual destructor on the base class.

C++ Weekly - Ep 270 - Break ABI to Save C++

I hope you learned something. I appreciate your feedback. Please reach out to me on X or via email.

Andreas