I am reading an example of cppreference at https://en.cppreference.com/w/cpp/language/constant_expression
Specifically, this one:
constexpr int incr(int& n)
{
return ++n;
}
constexpr int g(int k)
{
constexpr int x = incr(k); // error: incr(k) is not a core constant
// expression because lifetime of k
// began outside the expression incr(k)
return x;
}
constexpr int h(int k)
{
int x = incr(k); // OK: x is not required to be initialized
// with a core constant expression
return x;
}
constexpr int y = h(1); // OK: initializes y with the value 2
// h(1) is a core constant expression because
// the lifetime of k begins inside the expression h(1)
So, as far as I can tell, the issue with this code:
constexpr int x = incr(k);
is that it goes against this core expression rule: "modification of an object, unless the object has non-volatile literal type and its lifetime began within the evaluation of the expression".
But why does this instead work, even if the compiler evaluates it at compile-time the same way? (We can check this with std::is_constant_evaluated()
)
Isn't the compiler going against the "core expression rules" by evaluating this at compile time anyway?
int x = incr(k); // k lifetime didn't start in this expression, so incr should not be able to mutate it since the "core constant expression" rules disallow it
The issue here – albeit quite subtle – is that the constexpr
usage error is not for the specifier on either the g()
or h()
functions (all that states is that it is possible for them to be compile-time evaluated). The error is on the specific line, constexpr int x = incr(k);
in the g()
function (for the reason given).
In the h()
function, the analogous line doesn't have the constexpr
specifier … but that doesn't mean that it can't be evaluated at compile time. When calling h(1)
, the whole function can (and will) be evaluated at compile time, so there is no constexpr
conflict.
Change to using a call of h(x)
where the argument is not a literal (or other core constant expression) and you will see an error:
#include <iostream>
constexpr int incr(int& n)
{
return ++n;
}
constexpr int h(int k)
{
int x = incr(k); // OK: x is not required to be initialized
// with a core constant expression
return x;
}
int main()
{
constexpr int y = h(1); // OK - as explained on cppreference
int a = 42;
constexpr int z = h(a); // Illegal - "Variable 'a' cannot be used as a constant"
std::cout << y << " " << z << "\n"; // To prevent optimizing-away.
return 0;
}
From the same cppreference page that you linked (bold emphasis mine):
The
constexpr
specifier declares that it is possible to evaluate the value of the function or variable at compile time. Such variables and functions can then be used where only compile time constant expressions are allowed (provided that appropriate function arguments are given).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With