Idiomatic way to enforce constexpr-ness of constexpr functions

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.

Upvotes: 6

Views: 289

Answers (1)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275800

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.

Upvotes: 2

Related Questions