Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to test if a constexpr function is evaluated at compile time?

Since the extended versions of constexpr (I think from C++14) you can declare constexpr functions that could be used as "real" constexpr. That is, the code is executed at compile time or can behave as inline functions. So when can have this program:

#include <iostream>

constexpr int foo(const int s) {
  return s + 4;
}

int main()
{
    std::cout << foo(3) << std::endl;

    const int bar = 3;
    std::cout << foo(bar) << std::endl;

    constexpr int a = 3;
    std::cout << foo(a) << std::endl;

    return 0;
}

The result is:

7
7
7

So far so good.

Is there a way (possibly standard) to know inside foo(const int s) if the function is executed at compile time or at runtime?

EDIT: Also is it possible to know at runtime if a function was evaluated at compile time?

like image 558
LeDYoM Avatar asked Oct 24 '17 20:10

LeDYoM


People also ask

Is constexpr evaluated?

If you pass a non-constexpr argument to the PlusOne function, the compiler can't evaluate it at compile-time, and it will simply be a normal run-time function. Hint: "The constexpr specifier declares that it is possible to evaluate the value of the function or variable at compile time."

How do I know if a function is constexpr?

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.

Is constexpr guaranteed?

Quick A: constexpr guarantees compile-time evaluation is possible if operating on a compile-time value, and that compile-time evaluation will happen if a compile-time result is needed.

Can a function be constexpr?

A constexpr function is a function that can be invoked within a constant expression. A constexpr function must satisfy the following conditions: It is not virtual. Its return type is a literal type.

When is a const expression evaluated at compile time?

If the compiler is able to propagate constants up to the point where all arguments are constants, then the constexpr function will be evaluated at compile time. H Sutter said on Jan 14, 2013 03:48 PM: @chico: I didn't write the SO answer (I just edited it), but I'm not sure how it is different from Bjarne's one-line summary.

What is constexpr in C++?

constexpr stands for constant expression and is used to specify that a variable or function can be used in a constant expression, an expression that can be evaluated at compile time. The key point of constexpr is that it can be executed at compile time.

Can a function declared as constexpr be called during run-time?

Since it is possible that a function declared as constexpr can be called during run-time, under which criteria does the compiler decide whether to compute it at compile-time or during runtime? In this case, i is unknown at compile-time, which is probably the reason why the compiler treats POW () as a regular function which is called at runtime.

When is a function evaluated at compile time?

A constexpr function is evaluated at compile time if all its arguments are constant expressions. chico said on Jan 13, 2013 06:28 PM: ok, now we have B and H not agreeing how/when it should eval. confused.


2 Answers

C++20 introduces is_constant_evaluated, defined in header <type_traits>, which addresses this issue.

constexpr int foo(int s)
{
    if (std::is_constant_evaluated()) // note: not "if constexpr"
        /* evaluated at compile time */;
    else
        /* evaluated at run time */;
}

Note that here the ordinary if is used instead of if constexpr. If you use if constexpr, then the condition has to be evaluated at compile time, so is_constant_evaluated always returns true, rendering the test useless.

like image 158
L. F. Avatar answered Oct 19 '22 08:10

L. F.


The technique listed works, but since it uses static_assert it is not sfinae friendly. A better way (in theory, you'll see what I mean) to do this is to check whether a function is noexcept. Why? Because, constant expressions are always noexcept, even if the functions are not marked as such. So, consider the following code:

template <class T>
constexpr void test_helper(T&&) {}

#define IS_CONSTEXPR(...) noexcept(test_helper(__VA_ARGS__))

test_helper is constexpr, so it will be a constant expression as long as its argument is. If it's a constant expression, it will be noexcept, but otherwise it won't be (since it isn't marked as such).

So now let's define this:

double bar(double x) { return x; }

constexpr double foo(double x, bool b) {
    if (b) return x; 
    else return bar(x);
}

foo is only noexcept if the x is a constant expression, and b is true; if the boolean is false then we call a non constexpr function, ruining our constexpr-ness. So, let's test this:

double d = 0.0;

constexpr auto x = IS_CONSTEXPR(foo(3.0, true));
constexpr auto y = IS_CONSTEXPR(foo(3.0, false));
constexpr auto z = IS_CONSTEXPR(foo(d, true));

std::cerr << x << y << z;

It compiles, great! This gives us compile time booleans (not compile failures), which can be used for sfinae, for example.

The catch? Well, clang has a multi-year bug, and doesn't handle this correctly. gcc however, does. Live example: http://coliru.stacked-crooked.com/a/e7b037932c358149. It prints "100", as it should.

like image 26
Nir Friedman Avatar answered Oct 19 '22 10:10

Nir Friedman