Why constexpr method can return correctly class members whose value change during execution?

Viewed 88

I just discovered that a constexpr method can return correctly the value of a class member that changes during execution. My question is, how is this possible if constexpr methods are supposed to be evaluated completely at compile time?

The example below correctly outputs Value: 0 and then Value: 5. Even more, if I change the a.change(5) to something unpredictible for the compiler (such as a.change(atoi(argv[1])) or a.change(rand() % 10 + 1) it still works. Why? Why does it even compile?

#include <iostream>

class A
{
    public:
        void change(int value) { x = value; }
        int get() const { return x; }
        constexpr int value() const noexcept { return x; }
    private:
        int x{0};
};

int main()
{
    A a;

    std::cout << "Value: "  << a.get() << std::endl;
    a.change(5);
    std::cout << "Value: "  << a.get() << std::endl;
}

Thank you in advance

3 Answers

In your example you're not even calling the constexpr function, you're calling get, which is not constexpr.

However, constexpr functions can be evaluated at compile time, but also during run time if compile time evaluation is not possible.

If you called value instead of get, it would still work and the function would be evaluated at run time.

In order to force compile time evaluation, you can write

constexpr auto val = a.value();

This will indeed give you an error if compile time evaluation is not possible.

constexpr means that you can use that function in a core constant expression, as long as you have constexpr arguments for it. It can still be used in other contexts.

As it stands, you can't use A::value in a core constant expression, because you have no way of getting a constexpr A. If you instead had

class A
{
    public:
        constexpr A() = default;
        constexpr void change(int value) { x = value; }
        constexpr int get() const noexcept { return x; }
    private:
        int x{1};
};

You could write a function that modified an A at compile time.

constexpr int stuff(A a) {
    a.change(10);
    return a.get();
}

See it on coliru

constexpr functions in general are evaluated at compile-time only if they receive constexpr arguments, for methods it includes this pointer.

#include <iostream>

class A
{
    public:
        void change(int value) { x = value; }
        constexpr int value() const noexcept { return x; }
    private:
        int x{0};
};

int main()
{
    constexpr const A foo;
    const A bar;// Const it not enough.

    static_assert(foo.value()==0);
    static_assert(bar.value()==0);// ERROR:  non-constant condition for static assertion
}
Related