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 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 that would return the size of the actual memory that handle. 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;
}
};
Not 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 const
s is doesn't compile and having const
objects is 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 sounds like a feature everyone will use it 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
Top comments (1)
Great article, as always.
I just want
constexpr
in C2X. It seems such a nice language feature.