c++c++20constexprvirtual-functionsconstexpr-function

What is the advantage of constexpr virtual functions in C++20?


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?


Solution

  • 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:

    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.