When to use constexpr in C++?

Asked

Viewed 77 times

1

See you around the keyword constexpr (mainly in ifs and constants) many times, I have read the Microsoft documentation and searched on the internet and still did not understand much the function of it and when to use, wanted an explanation.

2 answers

4


I would start by reading the documentation which is considered almost official by everyone. Without mastering the documentation do not consider that you understand the mechanism, especially in C++ this is fundamental, this is a language that works and is right usually have a very large distance.

If I read it there, I think I can see that an answer here would be long and would in a way reproduce the documentation found there. If you make a very large summary in fact is not answering the question, give a reason and ignore all the others, staying without talking about all the restrictions ends up not helping so much. I decided to answer something because the answer currently accepts answers less than that.

In fact constexpr was created to make it clear to the compiler that it should be solved at compile time and therefore that expression can be optimized in various ways and even deleted from the executable.

It differs from const that does not establish a constant expression but that there is some possibly transitory constancy in it. One of the uses is to declare an identifier that will be used as a fixed value. In practice this use is equal to constexpr and indeed if it were not the legacy that keyword would be preferred, and const would be used in other contexts that I won’t even mention here because it’s not the focus.

This is perfectly valid:

constexpr int x = 10;
constexpr int y = x + 5;

The first one I could do with const, but not the second. Note that you can only do between constants, you cannot use variable.

In fact one of the most prominent uses is with if and is a more robust, powerful and flexible form of #if which was intended to be used earlier on the pre-processor that was legacy of C. So you say that you want the condition there to be solved by the compiler and the action to be used in the compilation is what’s inside the if or of else as the result during compilation. This gives a very great power of efficient programming by choosing only what matters for that case. This is very powerful especially with templates since their goal is to compile something according to the code being consumed. Before I had to juggle for this code to be compiled conditionally and now it’s easy and has a simple and intuitive syntax.

Example:

template <typename T>
auto get_value(T t) {
    if constexpr (is_pointer_v<T>)
        return *t;
    else
        return t;
}

This code will return a value or pointer to a value depending on the type of T be a pointer or not. Note that the function call will define how it will be compiled. There will be no processing cost, there will be two versions (if used in both forms) of the function and will be called the one that serve in each case. Then the if chose what to compile according to the rest of the code (the function call in case).

By making the function constant we can have the result if we pass a literal no processing cost on the execution:

template<int N>
constexpr int fibonacci() {
    if constexpr (N >= 2)
        return fibonacci<N - 1>() + fibonacci<N - 2>();
    else
        return N;
}

This function will be executed by the compiler, find the result and it will be used in generating the executable.

Note that you cannot pass a variable since its value is not known at runtime, even by gauge requirement.

If you don’t understand anything, it’s because you have to learn about templates. But it’s not the only place we can use constrexpr.

But we can use in simpler situations:

constexpr bool is_even(int num) { return num % 2 == 0; }

This will be solved at compile time if you pass a constant, but it will run at runtime if you pass a value that cannot be determined by the compiler.

But you can use it in more contexts, for example:

class Point {
    int x;
    int y;
    public:
        constexpr Point(int x, int y): x(x), y(y) {}
        constexpr int get_x() { return x; }
        constexpr int get_y() { return y; }
};

I put in the Github for future reference.

Note that even the constructor is constant, and since everything can constantly deliver the object is not created during execution, the compiler is able to create it in the compilation, provided, of course, that it has constant compilation data being passed to the constructor. So even a complex object can become a completely static value, which before could not be done.

One more complete reading can be seen on Soen, and is not the only good answer there.

3

The functions constexpr allow to return only one constant and run to build time. When we declare a variable const the function is evaluated directly for the same type and improves readability.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.