C++ constexpr function always evaluated at compile time? - c++

If I want to have a function that behaves the same as a macro, that is, compute the value at compile time, can I use a constexpr function?
For example, can I replace the Foo macro by the foo function and still have a compile time evaluated result in all of the following cases:
#define FOO(x) (x + 2)
constexpr int foo(int x) {
return x + 2;
}
void doSomething(int a) { ... }
int main() {
int res1 = foo(3);
doSomething(foo(4));
const int res2 = foo(5);
return 0;
}

With C++20, consteval might be your friend here:
consteval int foo(int x) {
return x + 2;
}
int main() {
constexpr int r = foo(2);
}

By themselves, constexpr functions are not required to be evaluated at compile time. However, you can force them to be evaluated by assigning the return value to a constexpr variable:
doSomething(foo(4)); // foo(4) not guaranteed to be evaluated at compile time
constexpr auto result = foo(4); // foo(4) _is_ guaranteed to be evaluated at compile time
doSomething(result):
Also, a note about your question on macros. A macro definition has nothing to do with compile time evaluation. It's more akin to an always inline function.

It's impossible to detect if something was evaluated at runtime or compile-time using only the C++ itself, so the as-if rule allows the compiler to do whatever it wants.
Nothing stops a compiler from performing the addition in your macro at runtime, and nothing stops it from calculating even a non-constexpr function at compile-time (as long as it doesn't perform any IO, etc). It all depends on optimization settings and the sanity of the compiler.
Normally, in unoptimized builds, constexpr functions are executed at runtime unless the return value is used in a context that requires a compile-time constant. This includes initializing a constexpr variable from it, and your const int res2 implicitly becomes constexpr because its initializer is constexpr, so foo(5) should be called at compile-time.
In optimized builds, you can expect the compiler to do as much as possible at compile-time. (A function doesn't even have constexpr, as long as the function body is visible in the current translation unit, or if link-time optimizations are enabled.)

Related

Constexpr evaluation and compiler optimization level

see the following snippet:
struct config {
int x;
constexpr int multiply() const {
return x*3;
}
};
constexpr config c = {.x = 1};
int main() {
int x = c.multiply();
return x;
}
If I compile this with clang and -O0 I get a function call to multiply even though the object c and the function are marked constexpr. If I compile it with -O1 everything gets optimized as expected. Gcc on the other hand generates no call to multiply.
If I change the main to:
int main() {
constexpr auto y = c.multiply();
int x = y;
return x;
}
If I compile this with clang and -O0 I get not function call and the value 3 directly as stack variable. The -O1 result is the same as above.
So my question is: Does the constexpr evaluation depend on the compiler level? I would expect that in the example 1 the call to multiply would be constexpr and performed compile time. (like gcc does)
BR,
go2sh
See https://godbolt.org/z/WvPE5W77h
The Standard just requires that a call to constexpr is evaluated at compile time if the arguments are constexpr and the result must be constexpr due to context. Basically just forcing more restrictions on the author of the function, thus allowing it to be used in constexpr contexts.
Meaning y in second snippet forces evaluation at compile time. On the other hand, x in the first is an ordinary run-time call.
But the as-if rule applies here - as long as the observable effects of the program remain the same, the compiler can generate any instructions it wants. It can evaluate any function, even non-constexpr ones if it can do so - happens in practice often with constants propagation.
Yes, in general, higher optimization levels will inline more code and push more evaluation to the compile time. But "looking at the assembly" is not an observable effect in the sense above so there are no guarantees. You can use inline to give a hint for inlining the function instead of calling it (constexpr is inline by default but for other reasons...) but the compilers can ignore them.
Of course, the compiler can evaluate all constexpr functions with constexpr args at compile time, that is why they exist, why clang does not do so with -O0, I do not know.
If you need guaranteed compile-time evaluation, use consteval instead.

What is the new feature about constexpr std::string with gcc 12.1

case1:
#include <string>
inline constexpr std::string test(std::string s) noexcept
{
return s + "xxx";
}
int main()
{
auto s = test("abc");
}
c++20 with gcc 12.1 is built okay, c++17 with gcc 12.1 or c++20/17 with gcc 11.1 was failed,
constexpr std::string, Is this a new feature, or what does this mean?
case2:
#include <string>
int main()
{
constexpr std::string test{"xxxxxx"};
}
And in this case both failed, what is the difference between these two cases.
There are two different use cases of constexpr here:
constexpr functions
constexpr objects
When you see it on a function such as
inline constexpr std::string test(std::string s) noexcept
{
return s + "xxx";
}
the constexpr is not part of the return type, in the same way that inline is not part of the return type; it is part of the function definition. In this case, constexpr says "this function can possibly be run at compile time".
The second use case you've mentioned is tagging an object definition as constexpr. In this use case, you're telling the compiler that this must be a compile time constant, and currently std::string objects cannot be marked constexpr due to the dynamic memory allocation that it does which it performed at runtime.
One thing you may have seen is that the std::string constructor was marked constexpr in C++20. This does not mean that you can create constexpr instances of std::string. It just means that std::string can be constructed and used within constexpr contexts (like a constexpr function).
You may then ask "if std::string requires runtime allocation, how can it be used within a constexpr function?". The basic answer is that the compiler uses a different allocation strategy to enable it, and does extra magic to make sure no undefined behaviour occurs. You can see more info here.
To give a bit more information about constexpr functions, note that I said it could "possibly" be run at compile time. A function marked constexpr can be run at either compile time or runtime, depending on the arguments it's called with
constexpr int double_val(int x) { return 2 * x; }
int main() {
const int y = double_val(4); // likely ran at compile time
int input = 0;
std::cin >> input;
const int z = double_val(input); // run at runtime
}
If you want to force the function to only run at compile time, that's what C++20's consteval keyword allows you to do.
consteval int double_val(int x) { return 2 * x; }
int main() {
const int y = double_val(4); // fine, ran at compile time
int input = 0;
std::cin >> input;
const int z = double_val(input); // ERROR, could not compile
}
The following might not compile either, for you:
constexpr auto s = test("abc");
There's a slight semantical difference between a constexpr object and a constexpr function.
A constexpr object must be a compile-time constant.
A constexpr function might be a compile-time constant if everything in the function is a constant expression. Subsequently, the result of the function is a constant expression if its parameters are, and obviously not if they're not.
What appears to be happening is that your compiler hasn't yet implemented the constexpr version of the std::string constructor in C++20, but has implemented the constexpr + operator.
Hence the function gets compiled without a constant expression as its parameter, but since its result is not assigned to a constexpr object this is not immediately apparent.
But assigning the function's result to a constexpr reveals the ugly truth.

constexpr and template compile time?

I have a few questions! I'm confused with template and constexpr and the difference.
I know templates are instantiated at compile time are they executed at compile time or only in run time? Is there a example where I can use them together to get some benefit?
And what is happening if we have a template with constexpr like in this example.
template <typename T>
constexpr T get_sum(T a, T b)
{
return a+b;
}
int main()
{
constexpr int a = get_sum(2,3); // compile time?
const float b = get_sum(2.2,3.2); // compile time?
float c = get_sum(2.2,3.2); // run time?
}
Your get_sum is a function template. get_sum<int> is a function almost like any other function. Don't get confused by template argument deduction, which does happen at compile time. Without deduction your main is exactly the same as:
constexpr int a=get_sum<int>(2,3);
const float b=get_sum<double>(2.2,3.2);
float c=get_sum<double>(2.2,3.2);
In a nutshell, templates get instantiated by the compiler when needed. Once the compiler synthesized a function, eg get_sum<int> this is a function like other functions and whether the function is constexpr is orthogonal to whether it is the result of instantiating a template.
constexpr on a function tells the compiler that the function can be evaluated at compile time. The compiler must evaluate it at compile time when called in a constexpr context. For example constexpr int a is initialized at compile time. A const float might be initilized already by the compiler. Even a (non-const) float might be optimized away completely by the compiler. There is nothing that prevents a compiler to optimize something as long as the observable behvior of the program is the same (none of your 3 variables is actually used).
Ergo:
int main()
{
constexpr int a=get_sum(2,3); // get_sum<int> must be called at compile time
const float b=get_sum(2.2,3.2); // get_sum<double> is likely to be called at compile time
float c=get_sum(2.2,3.2); // get_sum<double> might be called at compile time or runtime
// or not at all, because the call does not
// contribute to observable behavior
}
TL;DR
Whether a function is an intantiation of a function template and whether the function is constexpr are orthogonal.
For constexpr's there is also static_assert.
constexpr can be used both at compile time and runtime.
(C++20 has consteval, which only allows for compile time evaluation).
#include <cassert>
template<typename type_t>
constexpr auto sum(const type_t& value1, const type_t& value2)
{
return value1 + value2;
}
int main()
{
constexpr auto constexpr_value = sum(1, 2); // <== compile time
static_assert(constexpr_value == 3); // <== compile time validation
// or shorter
static_assert(sum(1, 2) == 3); // <== compile time evaluation
// constexpr's also can compile to runtime versions
auto value = sum(2, 3); // <== runtime evaluation
assert(value == 5);
return 0;
}

C++20 | std::is_constant_evaluated() and const variables

Let's consider the following code:
#include <type_traits>
int foo(int arg) {
if (std::is_constant_evaluated()) {
return 1;
} else {
return 0;
}
}
int main() {
const auto b = foo(0);
return b;
}
It returns 0 with both gcc and clang. I would have expected it to return 1 instead.
If foo() is made constexpr, while b is kept simply const, then it does return 1.
What am I missing here? Thanks!
std::is_constant_evaluated() returns true if and only if [meta.const.eval]:
evaluation of the call occurs within the evaluation of an expression or conversion that is manifestly constant-evaluated
This term, "manifestly constant-evaluated" (defined here), refers to contexts that have to be constant-evaluated. A call to a non-constexpr function (the nearest enclosing context here) is never constant evaluated, because it's non-constexpr, so this is straight-forwardly not "manifestly constant-evaluated."
Once we make it constexpr though, we're in this weird legacy quirk. Before C++11, which introduced constexpr, we could still do stuff like this:
template <int I> void f();
const int i = 42; // const, not constexpr
f<i>(); // ok
Basically, we have this carve out for specifically integral (and enumeration) types declared const that are initialized with a constant expression. Those still count as constant expressions.
So this:
const auto b = foo(0);
If foo(0) is an integral constant expression, then b is something that could be used as a compile time constant (and would be constant-initialized†, if it were at namespace scope). So what happens here is we do a two-step parse. We first try to evaluate foo(0) as if it were a constant expression and then, if that fails, fall back to not doing that.
In this first parse, with foo(0) evaluated as a constant, is_constant_evaluated() is (by definition) true, so we get 1. This parse succeeds, so we end up with b as a compile-time constant.
†For namespace-scope variables, constant-initialization is an important concept as well - to avoid the static initialization order fiasco. It leads to other gnarly examples (see P0595).
The important thing here is basically: is_constant_evaluated() should only be switched on to select a compile-time-safe algorithm vs a runtime algorithm, not to actually affect the semantics of the result.
You have to be a little careful with where and how you use is_constant_evaluated. There are 3 kinds of functions in C++, and is_constant_evaluated only makes sense in one of them.
// a strictly run-time function
int foo(int arg)
{
if (std::is_constant_evaluated()) // pointless: always false
// ...
}
// a strictly compile time function
consteval int foo(int arg)
{
if (std::is_constant_evaluated()) // pointless: always true
// ...
}
// both run-time and compile-time
constexpr int foo(int arg)
{
if (std::is_constant_evaluated()) // ok: depends on context in
// which `foo` is evaluated
// ...
}
Another common mistake worth pointing out is that is_constant_evaluated doesn't make any sense in an if constexpr condition either:
{
if constexpr (std::is_constant_evaluated()) // pointless: always true
// regardless of whether foo
// is run-time or compile-time
}

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

I was investigating some rather weird code-coverage results of constexpr functions (compile-time calls can't get instrumented by the code-coverage tool I use), and noticed that some constexpr functions were getting called as runtime functions, if the results of the function call were not stored.
It seems that, for constexpr functions or methods, if you store the results of the call (either in a runtime variable [emphasis!!!] or a constexpr variable), the call is a compile-time call (as long as the parameters are compile-time). If you ignore the results, the call is a runtime call. This has nothing to do with my code-coverage tool; the behavior seems to be repeatable in the simple example below.
You could argue that since constexpr functions cannot have side-effects, it doesn't matter what the compiler does if you don't return / use a result. I'd think that for efficiency, the compiler would still make whatever it can be constexpr, but that's neither here nor there... What I'm wondering is if this is even defined behavior.
Is this a portable way to guarantee your constexpr functions will be invoked as runtime??? There aren't a ton of uses for that, but one use is: if you want to "take credit for tests that were called on constexpr functions" in code coverage, by just calling the same function at the end of your unit test, and ignoring the result, so that they get instrumented.
There are other ways to force a function to get called as runtime, I know, I'm mostly curious about what's going on here. It was very unexpected when I first saw it. Unless this is portable, I'll probably just call my constexpr methods through a runtime object (which seems to do the trick even for static methods), or through a runtime function pointer.
See example below. Live demo: https://onlinegdb.com/rJao0RNGP
// Modified from https://stackoverflow.com/a/40410624/12854372
extern bool no_symbol;
struct ContextIsConstexpr {
size_t s;
constexpr ContextIsConstexpr() : s(1) {}
constexpr void operator()() {
auto ignore = s ? 1 : no_symbol;
}
};
constexpr bool tryIgnoringResult()
{
ContextIsConstexpr()();
return true;
}
constexpr void thereIsNoResult() {
ContextIsConstexpr()();
}
int main()
{
constexpr auto result1 = tryIgnoringResult(); // OK, compile-time
auto result2 = tryIgnoringResult(); // OK, compile-time
// tryIgnoringResult(); // Unexpected, runtime!
// thereIsNoResult(); // Unexpected, runtime!
}
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