Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Aren't all expressions during constant evaluation also constant expressions?

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
like image 833
SomeoneWithPassion Avatar asked Oct 20 '25 12:10

SomeoneWithPassion


1 Answers

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).

like image 107
Adrian Mole Avatar answered Oct 23 '25 02:10

Adrian Mole