Compile-Time or Runtime Detection Within a Constexpr Function

When does a constexpr function get evaluated at compile time?

constexpr functions will be evaluated at compile time when all its arguments are constant expressions and the result is used in a constant expression as well. A constant expression could be a literal (like 42), a non-type template argument (like N in template<class T, size_t N> class array;), an enum element declaration (like Blue in enum Color { Red, Blue, Green };, another variable declared constexpr, and so on.

They might be evaluated when all its arguments are constant expressions and the result is not used in a constant expression, but that is up to the implementation.

Constexpr functions not called at compile-time if result is ignored

It might be confusing, but constexpr functions should be called at compile time only in constexpr contexts (assignation to constexpr variable, use for array size or template parameter, ...).

In regular context, functions are called at runtime. Optimizer might resolve that function at compile time (as for any other functions following the as-if rule). constexpr is indeed a good hint for optimization to happen.

You could argue that since constexpr functions cannot have side-effects

They can have side effect, see following valid example:

constexpr int f(int i)
{
if (i == 0) return 0;
std::cout << i << std::endl;
return i;
}

int main()
{
[[maybe_unused]] constexpr int zero = f(0); // Compile time
f(42); // runtime
}

Demo

How to understand constexpr function has everything it needs to compute its result at compile-time?

But per my understanding, for the first case, the argc value also can't be known during compile-time

You're confusing "parameters" with "arguments".

Parameters are the things in the function declaration that represent stuff the user passes in. Arguments are the actual stuff the user passes in at the site of a particular function call.

argc is an argument. The value of this may or may not be a constant expression, and if it is not, a constexpr function is fine.

int is used as the type for a parameter to add. int is a type which can be a constant expression. Now, not every int value is a constant expression, but it is a type that could theoretically be one.

Therefore, add can be a constexpr function, because its parameter list uses types which can be constant expressions. This is about the nature of the function itself.

The type vector<T> can never be a constant expression (in C++17, at any rate). As such, no constexpr function can take one as a parameter. Nor can it use one internally.

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

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.

Why is initialization of variable with constexpr evaluated at runtime instead of at compile time

By inspecting the generated assembly, we can confirm that in both cases G++ 7.5 computed the fib(32) value at compile time:

    movl    $2178309, %esi

The reason G++ evaluates constexpr context so fast is due to memoization which it performs when evaluating constexpr and template contexts.

Memoization completely kills fibonacci computational complexity by reducing it to O(N) complexity.

So why then is non-constexpr evaluation so much slower? I presume that's a bug/shortcoming in the optimizer. If I try with G++ 8.1 or later, there's no difference in compilation times, so presumably it had already been addressed.

constexpr function gets value at compile time even though my variable is not constexpr

ipSourceAddress does not get its value at compile time (as-if rule nonwithstanding).

ip's value is not usable in a constant expression since it is not declared constexpr and doesn't qualify for one of the other exceptions to the rule of lvalue-to-rvalue conversion in a constant expression. Therefore IPAddr::inet_pton<AF_INET>(ip) is not a constant expression.

You can see this clearly by making ipSourceAddress constexpr (which auto does not imply).

constexpr on a variable requires initialization at compile-time and because the initializer is not a constant expression it will fail.

See godbolt.

I don't know how you come to a different conclusion.


Note however that the library does seem to require that the char array passed to it is exactly as long as the string it contains (plus null-terminator). It fails if you hand it a longer array and outputs the value you are seeing.

See godbolt.

It seems the author intends the functions to only be called directly with string literals.

What is preventing compile time evaluation of this constexpr function?

You have set optimisation to level 1 in Godbolt! Try -O3 instead of -O1.



Related Topics



Leave a reply



Submit