Contents

C++20's constexpr virtual functions can simplify templates code

Contents

Recently, I’ve learned about constexpr virtual functions. The support for which was added along with C++20. C++23 additionally allows for constexpr new (with some restrictions). There’s a great article about constexpr virtual on C++ stories blog, followed by another great post about constexpr new. I highly recommend reading Bartek’s posts.

Wanting to add this new feature to my tool belt I was wondering what would be the best application for constexpr virtual and how would I use them in my code.

The example code that Bartek used exists in its entirety in a single translation unit. This is not how I normally write code relying on inheritance and virtual dispatch.

Most of the time, I’m declaring an abstract interfaces in the header file and the actual implementation is hidden within separate *.cpp files - this is in order to hide implementation details and simplify ABI. constexpr implies inline so, it won’t work with that kind of code - the definition has to be available along with the declaration.

It’s very similar to templates and this gave me an idea about a perfect application for constexpr virtual.

Tip
You can simplify value specialised template code with constexpr virtual.

Consider a value specialised template code. Something similar to the below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
template <int N> struct Fib {
  static constexpr int value = Fib<N - 1>::value + Fib<N - 2>::value;
};

template <> struct Fib<1> {
  static constexpr int value = 1;
};

template <> struct Fib<0> {
  static constexpr int value = 0;
};

static_assert(Fib<15>::value == 610, "");

The code is simple but it’s greatly obscured by the templates syntax. Often, templates are specialised on values to implement something alike “strategy” pattern:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
template <bool IsRequestV> struct Message {
  static constexpr int get() { return 123; }
};

template <> struct Message<false> {
  static constexpr int get() { return 456; }
};

using Request = Message<true>;

using Reply = Message<false>;

With constexpr virtual the above code looks much more concise:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct Message {
  constexpr virtual ~Message() = default;

  constexpr virtual int get() const = 0;
};

struct Request : Message {
  constexpr virtual int get() const override { return 123; }
};

struct Reply : Message {
  constexpr virtual int get() const override { return 456; }
};

constexpr int makeMessage(bool isRequest) {
  auto *msg = isRequest ? static_cast<Message *>(new Request)
                        : static_cast<Message *>(new Reply);
  auto r = msg->get();
  delete msg;
  return r;
}

static_assert(makeMessage(true) == 123, "");
static_assert(makeMessage(false) == 456, "");

Maybe it’s just me, but this looks “simpler” as there’s no additional mental gymnastics required to understand template specialisations. It feels much more traditional and just like “normal” procedural code.

Conclusion

This above is, of course, a very trivial and contrived example code but it definitely exemplifies the concept and an application for constexpr virtual.