Once, I wondered if we could have virtual constexpr functions. It is now possible with C++20 so let’s try!

A Piece of Personal Experience#

Two years ago, I had several classes with a common point: base classes had a pure virtual function to request derived classes to return a constant. Something like this:

struct Memory {
    // Capacity in bytes
    virtual unsigned int capacity() const = 0;
};

It was the base class for drivers to communicate with a particular memory chip. Each subclass would return the size of the actual memory it handles. For instance, for the Microchip’s 25LC160C EEPROM:

struct EEPROM_25LC160C : Memory {
    unsigned int capacity() const override {
        return 2048;
    }
};

I asked myself: can I make this function constexpr because the size never changes for each class? Answer: it was not possible back then.

Now with virtual constexpr#

But guess what? This is now possible in C++20:

A constexpr function must satisfy the following requirements:

  • it must not be virtual (until C++20)
  • (…)

Code can now be:

struct Memory {
    virtual constexpr unsigned int capacity() const = 0;
};

struct EEPROM_25LC160C : Memory {
    constexpr unsigned int capacity() const override {
        return 2048;
    }
};

Note that it is possible for the base function to be constexpr while the derived function is not. And vice versa.

Examples#

Great! But wait… How is this useful for me? Well, actually, for the moment, I don’t have a clue 😅

Code like this works:

constexpr EEPROM_25LC160C eeprom;
static_assert(eeprom.capacity() > 1024);

Before virtual constexpr, I would have to provide a public static constexpr member like this:

struct EEPROM_25LC160C : Memory {
    static constexpr auto SIZE = 2048;

    unsigned int capacity() const override {
        return SIZE;
    }
};

static_assert(EEPROM_25LC160C::SIZE > 0);

C++20’s version is an improvement but I think it’s minor.

Another possible code:

static constexpr EEPROM_25LC160C eeprom;

constexpr const Memory& getMemory() {
    return eeprom;
}
 
int main() {
    constexpr const Memory& memory = getMemory();
    static_assert(memory.capacity() > 1024);
}

OK… But without the consts it doesn’t compile and having const objects is not necessarily what I want.

Here is an example that doesn’t compile (obviously):

template<unsigned int startAddress, unsigned int bytesCount>
class Serializer {
private:
    EEPROM_25LC160C eeprom;
    static_assert(startAddress + bytesCount < eeprom.capacity());
};
 
int main() {
    constexpr EEPROM_25LC160C eeprom;
    Serializer<1500, 700> serializer;
}
<source>:15:47: error: invalid use of non-static data member 'memory'

    static_assert(startAddress + bytesCount < memory.capacity());

                                              ^~~~~~

I can try something else:

template<unsigned int startAddress, unsigned int bytesCount>
class Serializer {
public:
    constexpr Serializer() {        
        static_assert(startAddress + bytesCount < eeprom.capacity());
    }

private:
    EEPROM_25LC160C eeprom;
};
 
int main() {
    constexpr EEPROM_25LC160C eeprom;
    constexpr Serializer<1500, 700> serializer;
}

Still, the code doesn’t compile:

<source>:15:23: error: static_assert expression is not an integral constant expression

        static_assert(startAddress + bytesCount < eeprom.capacity());
                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Conclusion#

I have read the paper that proposed to add this feature to the C++ standard: “Allowing Virtual Function Calls in Constant Expressions” by Peter Dimov and Vassil Vassilev.

Clearly, it doesn’t sound like a feature everyone will use in their everyday code. I will keep it in mind and I hope one day I will find a use-case in my code 😁

PS: here is an excellent article from Microsoft about the spaceship operator: Simplify Your Code With Rocket Science: C++20’s Spaceship Operator