Consider the following example code :
#include <tuple>
void blah();
int buh;
constexpr auto get()
{
return std::get<0>(std::make_tuple(&blah, &buh));
}
int main()
{
get();
}
One would expect that since the function get()
is a constant-expression, it would return a constant.
That's not what happens: std::make_tuple
, std::get
are instantiated and called: https://godbolt.org/g/PkHrTp
Now, if we replace the implementation of get()
by
constexpr auto get()
{
constexpr auto x = std::get<0>(std::make_tuple(&blah, &buh));
return x;
}
We get the expected behaviour : the parameter x
's computation is optimized out, even at -O0, and make_tuple
, get
are not instantiated, which can be fairly useful to reduce binary bloat.
Is there an idiomatic way to enforce that functions of the form constexpr auto foo()
always behave like in the second example ?
For now I would resort to :
#define constexpr_return(X) do { constexpr auto constexpr_return_x_ = X; return constexpr_return_x_; } while(0)
constexpr_return(std::get<0>(std::make_tuple(&blah, &buh)));
for instance but I don't know if this is optimal.
A constexpr function is one whose return value is computable at compile time when consuming code requires it. Consuming code requires the return value at compile time to initialize a constexpr variable, or to provide a non-type template argument.
The easiest way to check whether a function (e.g., foo ) is constexpr is to assign its return value to a constexpr as below: constexpr auto i = foo(); if the returned value is not constexpr compilation will fail.
#define directives create macro substitution, while constexpr variables are special type of variables. They literally have nothing in common beside the fact that before constexpr (or even const ) variables were available, macros were sometimes used when currently constexpr variable can be used.
A const int var can be dynamically set to a value at runtime and once it is set to that value, it can no longer be changed. A constexpr int var cannot be dynamically set at runtime, but rather, at compile time. And once it is set to that value, it can no longer be changed.
template<auto x>
std::integral_constant< std::decay_t<decltype(x)>, x > k{};
non-type template parameters must practically be evaluated at compile time.
k<get()>
or in certain corner cases k<get()>()
probably does what you want.
This doesn't work for constexpr
values that cannot be passed as non-type template parameters, but it does work for integers and pointers and function pointers.
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