Constexpr in C++

Pages: 12
Hello all,

Is what I know of constexpr written below all correct? Should add something to it, please?

- A constexpr symbolic constant must be given a value that is known at compile time whereas a const works for both compile and run times.

- const: meaning roughly “I promise not to change this value.” This is used primarily to specify interfaces so that data can be passed to functions using pointers and references without fear of it being modified. The compiler enforces the promise made by const. The value of a const can be calculated at run time.

- constexpr: meaning roughly “to be evaluated at compile time.” This is used primarily to specify constants, to allow placement of data in read-only memory (where it is unlikely to be corrupted), and for performance. The value of a constexpr must be calculated by the compiler.

- A constexpr function can be used for non-constant arguments, but when it's used this way, the result is not a constant expression. We allow a constexpr function to be called with non-const-expression arguments in contexts that don not require constant expressions. That way, we don't have to define essentially the same functions twice: once for constant expressions and once for variables. If a function may have to be evaluated at compile time, declare it constexpr.

- The body of constexpr functions is evaluated at compile time only if the result is used in a const/constexpr expression.

- constexpr can be used with constructors and objects. The arguments and the return type, that way, must have literal values only.
Last edited on
> Should add something to it, please?

Perhaps add the restrictions on constructs that are allowed in the body of a constexpr function.
More info: https://en.cppreference.com/w/cpp/language/constexpr


Related:

constexpr if statement:
https://en.cppreference.com/w/cpp/language/if#Constexpr_If

consteval (C++20):
https://en.cppreference.com/w/cpp/language/consteval

Looks good.

The only nit to pick is with point four. The result is a constexpr; but as with any constant value it can be assigned to a mutable object. In other words:

1
2
3
4
5
  int x = 7;
  //  ↑   ↑
  //  ↑   constexpr
  //  ↑
  //  mutable object 

Hope this helps.
@JLBorges
Thanks.
I actually can't understand cppreference whatsoever! (And never could)
If possible, using a simple language like the way I used, add the point to the list, please?

I even forgot the meaning of LiteralTypes. I think they're fixed values. like: 2, 4.3, -14, 0.0023, true/false .... (I hope it's correct)

@Duthomhas
Thanks, I got it.
Now I imagine that constexpr is nothing in its all contexts but a fixed value like the values above. But then it will conflict with LiteralType! :)
Last edited on
> the meaning of LiteralTypes. I think they're fixed values. like: 2, 4.3, -14, 0.0023, true/false ....

Also references, enumerations and simple (constexpr constructible) class types. Also, arrays of literal types.


> If possible, using a simple language like the way I used, add the point to the list, please?

Something like this, perhaps:

The body of a constexpr function should not have:
a. definitions of uninitialised variables eg. int i ; // uninitialised
b. definitions of variables with static or thread storage duration
c. definitions of variables which are not of literal types
d. labels other than case labels
e. goto or asm statements
The difference between a literal value and a constexpr is where it starts being an immutable value.

A literal never changes value. Ever. A 7 is a seven from before the compiler ever sees it.

A constexpr is mutable — as long as the compiler is messing with it. Once your program is compiled, it cannot be changed.


Example time:

 
  int x = 7;

“7” is a literal — the compiler cannot change it, it has no need to change it, etc.

 
  int x = 2 * 5 - 3;

“2 * 5 - 3” is a constant expression, composed of constexpr operations over literal values.
That is, the compiler knows how to compute multiply and subtract using integer operands, and the operands themselves are literal integer values. Hence, all the computation can be done before your program ever executes, by the compiler itself.

Once your program executes, the initializer value 7 is immutable. The variable x, however, is not; you treat it as any other mutable variable.

[edit]
To expand on this, then, it is possible to write a function the compiler executes, can be used to generate different values in different places in the code. We call this a constexpr.

Hope this helps.
Last edited on
Literals are tokens in the source code; to modify a literal, the source code needs to be modified.

An object of a user-defined literal type may be modifiable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
#include <type_traits>

struct A
{
    constexpr A( unsigned long long x, int y ) : x(x), y(y) {}

    A& zero()
    {
        std::cout << "zeroing object " << *this << " at address " << this << '\n' ;
        x = y = 0 ;
        return *this ;
    }

    unsigned long long x ;
    int y ;

    friend std::ostream& operator<< ( std::ostream& stm, const A& a )
    { return stm << "A{ " << a.x << ", " << a.y << " }" ; }
};

constexpr A operator"" _xy( unsigned long long xy )
{ return { xy/1000, int(xy%1000) } ; }

template < typename T > void print_stuff()
{
    std::cout << std::boolalpha
              << "object type? " << std::is_object<T>::value << '\n'

              << "scalar type? " << std::is_scalar<T>::value << '\n'

              << "literal type? "
              << std::is_literal_type<T>::value << "\n\n" ;
}

#define PRINT_STUFF(x) { std::cout << #x << '\n' ; print_stuff< decltype(x) >() ; }

int main()
{
    PRINT_STUFF(1234) ; // object type, scalar type, literal type

    PRINT_STUFF(79552476868968_xy) ; // object type, non-scalar type, literal type

    // modify an anonymous object of a literal type
    std::cout << 123456_xy .zero() << '\n' ; // zeroing object A{ 123, 456 } at address ...
                                             // A{ 0, 0 }
}

http://coliru.stacked-crooked.com/a/abb26816ae9a4bd8
https://rextester.com/XURVSY36941
Trying to avoid confusing the OP further, JLBorges is talking about making your own literal type, where you can declare what a literal looks like and write a function the compiler can use (a constexpr function, given in his his example) to create an object (like a variable) your program can play with when it runs.
Last edited on
JLBorges wrote:
The body of a constexpr function should not have:
[...]
e. goto or asm statements

asm statements are going to be allowed in C++20 (as weird as it sounds) - p1668r1 got accepted in Koln (executing that inline assembly at compile time won't be allowed, though)
Last edited on
Thank you!
@Duthomhas

When is a constexpr a mutable value and when is it an immutable value, please? (With example please)



When is a constexpr a mutable value and when is it an immutable value, please? (With example please)
It is mutable at compiletime. Not at runtime.
So why does VS give this error for ci = 6; even without compiling/debugging the code?

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
using namespace std;

int main()
{
	constexpr int ci = 5;
	ci = 6;
	cout << ci << endl;

	cin.get();
	return 0;
}


Expression must be a modifiable lvalue.
I think what Duthomhas mean is that constexpr variables are mutable while they are being initialized (i.e. inside the constructor). After the object has been fully constructed it cannot be modified.
Close, but not quite.

What modern versions of C++ have done is add a kind of meta-coding capability to the language, which is to say, you can write code for the compiler to execute, before your program ever runs.


The key is to remember that there are two distinct processes that code can target:

  • code the generated executable runs
  • code the compiler runs

This second possibility occurred because people playing around with templates quickly discovered that you can make the compiler do a lot of things that we previously would only do in the final, generated executable. Constexpr simply takes that to the next, official level.

A nice example of using template meta-programming to compute a value during compilation can be found here: http://www.cplusplus.com/forum/general/59883/2/#msg326710
(program to compute the number of digits from 1 to N).

──────────────────────────────
 
There is no such thing as a constexpr variable (which is a nice oxymoron, thanks!).

The value computed at any one step in a constexpr may be further modified until the final value is computed and written to the executable. Again, to the simplest possible example:

    constexpr int x = 2 * 5 - 3;

is computed in (at least) two steps: (2 * 5) → 10, then (10 - 3) → 7. That intermediate value (the ‘10’) must be preserved somewhere during the computation, most likely in a register value, but not necessarily. It is also possible the compiler stores the result of the subtraction in yet another place in computer memory, but it is again more likely that the compiler simply replaces the 10 value with the new sum. The underlying code might even be expressed as something like:

    constexprs['x'] = 2;
    constexprs['x'] *= 5;
    constexprs['x'] -= 3;
 
Next, when the compiler finds your code using the constexpr value:

    cout << x << "\n";

It can lookup the x in its table of constexpr values and perform a substitution:

    cout << 7 << "\n";

and compile that into your program’s executable.

──────────────────────────────
 
That is different than creating a variable used by your program’s final executable:

    int x = 2 * 5 - 3;

In this case, the compiler must still compute the value of the expression 2 * 5 - 3. (And, while this computation could be done by your executable, C and C++ compilers, at least, will do it for you so that no time is spent doing it when your executable runs.) Performing the proper computation and substitution:

    int x = 7;

While your code is running, x is still a variable like any other, which your executable may modify and use at its leisure.

──────────────────────────────
 
As a final note, the simple examples I have been using (without the constexpr keyword) aren’t technically constexprs. They are what is called “constant folding”. The concept of when code is evaluated/executed/computed between the two are the same, though, so we can safely ignore the differences in this context. Just be aware, however, that I have blurred some lines.

Hope this helps.
There is no such thing as a constexpr variable (which is a nice oxymoron, thanks!).
That's not helpful. Use C++'s terminology when discussing C++.
A variable is created by the declaration of a reference or object. A variable declared with constexpr is a constexpr variable. What do you call it otherwise?
Last edited on
A constexpr constant.

-Albatross
Well, that's an improvement, even if it's not precise.

Describing things as "constexprs" seems like a speedy way to conflate constexpr with const, constant expressions, and the somewhat larger set of variables (which could be called constants, I guess) whose values are usable in constant expressions.

I think the cppreference page says it best:
constexpr - specifies that the value of a variable or function can appear in constant expressions

https://en.cppreference.com/w/cpp/language/constexpr
Note its (and the standard's) use of the phrase "constexpr variable".

There is a (not merely pedantic) distinction between a variable and its value. That is to say, there are language rules that play into that distinction. For instance:
1
2
3
4
5
6
7
8
void g() {
  const int n = 0; // NOTE(mbozzi): n is usable in a constant expression
  [=] {
    // NOTE(mbozzi): odr-usage is a usage requiring a definition 
    constexpr int i = n;        // OK, n is not odr-used here
    constexpr int j = *&n;      // ill-formed, &n would be an odr-use of n
  };
}

http://eel.is/c++draft/expr.const#4.13
Last edited on
Well, the majority of statements above were having dozens of ambiguities, but part of them was a shade helpful for me as the OP.

constexpr is a keyword used for both variables and constants. And it's for a performance improvement because it's evaluated at compile time. Now the developer will desire to use it for all code! Why not? More speed at the end. But it's not viable I guess!

Now, still in simple language, where to use constexpr for a type or function properly, please?

> where to use constexpr for a type or function properly

constexpr variable:
variable of a literal type that is explicitly initialised with an an expression that is evaluated at compile time.

constexpr function:
The intent of the programmer is that the function would be evaluated at compile time
for some suitable subset of argument values.

constexpr constructor:
The intent of the programmer is that the constructor can be used for constant initialisation
(a compile-time-calculated object representation can be stored as part of the program image)
for some suitable subset argument values.

Absence of constexpr:
There is no programmer intent that this must be evaluated at compile time;
a call of this function is never going to be used in a constant expression.

C++20: consteval function:
A constexpr function with the additional constraint that every call to this function must produce a constant expression (must be evaluated at compile time).

In short, if the design intent is:
'at least in some situations, the function must be evaluated at compile time', mark it as constexpr

C++20:
If the intent is: 'the function must always be evaluated at compile time', mark it as consteval
Pages: 12