I'd like to have a type that behaves differently whether it is used in a constexpr context.
See this example:
#include <cstdio>
#include <type_traits>
template <bool IS_CONSTANT_EVAL>
struct Bar;
template <>
struct Bar<false> {
constexpr int doIt() const {
return 0;
}
};
template <>
struct Bar<true> {
constexpr int doIt() const {
return 1;
}
};
constexpr int foo() {
if (std::is_constant_evaluated()) {
Bar<true> v;
return v.doIt();
} else {
Bar<false> v;
return v.doIt();
}
}
int main() {
int rt = foo();
constexpr int ce = foo();
printf("Runtime: %d ConstEval: %d \n", rt, ce);
}
In this example, foo behaves differently whether it's called from a constexpr context, because the specialization depends on constexpr-ness, so it prints:
Runtime: 0 ConstEval: 1
But if I try to simplify this to:
constexpr int foo() {
Bar<std::is_constant_evaluated()> v;
return v.doIt();
}
It doesn't work anymore, because std::is_constant_evaluated() always returns true as a template argument.
Even, I'd like to have an even simpler foo, something like this:
constexpr int foo() {
Bar v;
return v.doIt();
}
So Bar itself should deal with constexpr-ness somehow, I don't want to repeat std::is_constant_evaluated() at each Bar usage.
Is it possible to do this?
Note: I actually don't want to have different behavior, just different implementation. Because of constexpr limitations, I need to have different implementations of Bar depending on constexpr-ness. For example, memcpy and reinterpret_cast make expressions not constant. So, if Bar is used in a constant expression, I need to use some constexpr-compatible technique to replace memcpy or reinterpret_cast.
Note 2: I really need different Bar definitions, it's not just doIt that needs to differ. Replacing memcpy and reinterpret_cast needs extra members in Bar if it is used in a constant expression.
This is a shot in the dark, since the actual classes in question may not permit this. But C++20 relaxed constant expression by a very large factor. Specifically, with virtual function call being allowed now, you can do something like this:
class Bar {
struct impl {
constexpr virtual int doIt () const = 0;
};
struct : impl {
constexpr int doIt() const override { return 1; }
} _c;
struct : impl {
constexpr int doIt() const override { return 0; }
} _r;
impl* pimpl;
public:
constexpr Bar() {
if(std::is_constant_evaluated()) {
pimpl = &_c;
}
else {
pimpl = &_r;
}
}
constexpr int doIt() const {
return pimpl->doIt();
}
};
constexpr int foo() {
Bar bar;
return bar.doIt();
}
Live
Not sure how to call this idiom. It's kinda like type erasure, but the types aren't really erased... The constructor chooses which implementation to use (once) based on the context it is executed in, and everything else delegates to that implementation via an interface.
The caveat is of course that it's okay for _r's members to not be usable in constant expressions, but the constructor must be (for obvious reasons), and so this is why I said it may not apply.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With