0

I can easily say that by declaring a function as constexpr, we evaluate it during the compile-time and this saves time during run-time as the result was already produced.

On the other hand, virtual functions need to be resolved during run-time. Hence, I guess we cannot get rid of the resolution process. Only the result can be fetched quickly thanks to the mechanism of constexpr functions.

Is there any other benefit of constexpr virtual functions?

Caglayan DOKME
  • 948
  • 8
  • 21
  • 5
    Be careful with your first statement. `constexpr` **allows** functions to be executed at compile time, it **doesn't enforce** it. If you want to enforce it, you'll have to use `consteval`. – Timo Dec 21 '21 at 13:52
  • 5
    _"virtual functions need to be resolved during run-time."_ A common assumption, but this is not true. They will be resolved at runtime if the type is not known. If the type is known, the compiler can resolve it. Consider `ConcreteType c; c.virtualfunc();` Does the runtime need to check which function that is? – Drew Dormann Dec 21 '21 at 13:52
  • 4
    Does this answer your question? [Can virtual functions be constexpr?](https://stackoverflow.com/questions/34828161/can-virtual-functions-be-constexpr) – dlask Dec 21 '21 at 13:52
  • @DrewDormann Yes, it's a case where we don't actually need the virtuality. I was thinking of pointer types. Thanks anyway – Caglayan DOKME Dec 21 '21 at 17:34

1 Answers1

4

Well the obvious benefit is that you can even do virtual function calls at compile time now.

struct Base {
    constexpr virtual int get() { return 1; }
    virtual ~Base() = default;
};

struct Child : Base {
    constexpr int get() override { return 2; }
};

constexpr int foo(bool b) {
    Base* ptr = b ? new Base() : new Child();
    auto res = ptr->get(); // this call is not possible prior to C++20
    delete ptr;

    return res;
}

constexpr auto BaseVal = foo(true);
constexpr auto ChildVal = foo(false);

You can't use the get function via a base pointer in a constant expression prior to C++20. If you make it constexpr, you can though. Example.


Now thinking about what benefit we could get from virtual function calls at compile time: maybe compile times. C++ has basically two mechanisms to deal with polymorphism:

  • templates, and
  • virtual functions.

Both solve essentially the same problems but at different stages in your program's life time. Of course it's nice to do as much computation as possible at compile time and therefore have the best performance at run time. However, this is not always a feasible approach because compile time can explode quickly due to how templates work.

Speculations start here. Now what if we broaden the stages at which virtual functions can be called and also allow them to be called at compile time? This would allow us, in some cases, to replace heavily recursive or nested templates with virtual function calls. Assuming that the constexpr interpreter is faster than the compiler recursively resolving templates, you could see some compile time reductions.

Of course this benefit is overshadowed by the performance increases you'll get from concepts and modules.


Another benefit lies in the nature of constexpr in general: UB is forbidden during constant evaluation. This means you could check if your virtual functions are UB free with a few static asserts.

Timo
  • 9,269
  • 2
  • 28
  • 58
  • i would add the working example code also in the question, rather than only the "this is not how it works" code – 463035818_is_not_an_ai Dec 21 '21 at 14:20
  • 2
    More important than compile-times is that template metaprogramming logic (while it *can* compute anything) is very opaque and is essentially a different language (functional and pure with its own data structures and syntax etc.) from "actual" C++. If you listen to talks from the C++ committee etc. the principal reason for "constexpr everything" is always just "so compile-time C++ can look like and be understood like normal C++". Sidestepping the "template language"'s horribly inefficient "execution model" is a bonus. – HTNW Dec 21 '21 at 14:52
  • @HTNW you're right. I thought about the readability aspect as well but when I reached the end of my speculations I had already forgotten that point. I guess my compile time interpreter has some memory leaks. Oh well... – Timo Dec 21 '21 at 14:58
  • @HTNW Why is the "execution model" of "template language" horribly inefficient? – Caglayan DOKME Dec 21 '21 at 17:53
  • 1
    @CaglayanDOKME Template "programs" are executing by instantiating oodles of templates, so the compiler has to process tons of class and function definitions that will probably never appear at runtime. E.g. a simple template-level insertion sort I've just tested can generate >260 classes to sort 10 elements. – HTNW Dec 21 '21 at 20:19