Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forcing a constant expression to be evaluated during compile-time?

A few days ago I asked by which criteria the compiler decides whether or not, to compute a constexpr function during compile time.

When does a constexpr function get evaluated at compile time?

As it turns out, a constexpr is only evaluated during compile-time, if all parameters are constant expressions and the variable you are assigning it to is are constant expression as well.

template<typename base_t, typename expo_t>
constexpr base_t POW(base_t base, expo_t expo)
{
    return (expo != 0 )? base * POW(base, expo -1) : 1;
}

template<typename T>
void foobar(T val)
{
    std::cout << val << std::endl;
}

int main(int argc, char** argv)
{
    foobar(POW((unsigned long long)2, 63));
    return 0;
}

If what I was told is true, this code example is very impratical, since foobar doesn't take a constexpr (you can't use consexpr for parameters for some reason), POW gets evaluated during runtime, even though it would have been possible to compute it during compile-time. The obvious solution for forcing a compile-time evaluation would be this:

auto expr = POW((unsigned long long)2, 63);
foobar(expr);

This however forces me to use an additional line of code, which shouldn't be necessary each time I want to make sure a constexpr gets evaluated during compile-time. To make this a little more convenient, I've come up with the following dubious macro:

#define FORCE_CT_EVAL(func) [](){constexpr auto ___expr = func; return std::move(___expr);}()
foobar(FORCE_CT_EVAL(POW((unsigned long long)2, 63)));

Despite the fact that it works just fine, I feel like as if something isn't right about it. Does creating an anonymous lambda impact performance? Does returning by rvalue reference actually move the expression to the function parameter? How does std::move impact performance? Is there a better one liner solution for this?

like image 343
Byzantian Avatar asked Jan 12 '13 14:01

Byzantian


People also ask

Is constant expressions are evaluated at compile time?

A constant expression gets evaluated at compile time, not run time, and can be used in any place that a constant can be used. The constant expression must evaluate to a constant that is in the range of representable values for that type.

Is constexpr always evaluated at compile time?

constexpr indicates that the value, or return value, is constant and, where possible, is computed at compile time.

Is const compile time?

Difference between Run-time and Compile-time constantsA compile-time constant is a value that is computed at the compilation-time. Whereas, A runtime constant is a value that is computed only at the time when the program is running. 2.

In which the response to a function is determined at the compile time?

In static polymorphism, the response to a function is determined at the compile time. In dynamic polymorphism, it is decided at run-time.


1 Answers

Just to not leave it buried in comments:

#include <type_traits>

#define COMPILATION_EVAL(e) (std::integral_constant<decltype(e), e>::value)

constexpr int f(int i){return i;}

int main()
{
    int x = COMPILATION_EVAL(f(0));
}

EDIT1:

One caveat with this approach, constexpr functions can accept floating-point and be assigned to constexpr floating-point variables, but you cannot use a floating-point type as a non-type template parameter. Also, same limitations for other kinds of literals.

Your lambda would work for that, but I guess you would need a default-capture to get meaningful error message when non-constexpr stuff get passed to the function. That ending std::move is dispensable.

EDIT2:

Err, your lambda approach doesn't work for me, I just realized, how it can even work, the lambda is not a constexpr function. It should not be working for you too.

It seems there's really no way around it but initializing a constexpr variable in local scope.

EDIT3:

Oh, ok, my bad, the purpose of the lambda is just the evaluation. So it's working for that. Its result, instead, is which is unusable to follow another compilation time eval.

EDIT4:

On C++17, lambdas now can be used in constexpr contexts, so the limitation referred to in EDIT2/EDIT3 is removed! So the lambda solution is the correct one. See this comment for more information.

like image 128
pepper_chico Avatar answered Oct 19 '22 17:10

pepper_chico