Poeta Kodu
Poeta Kodu

Reputation: 1160

C++ - Can I create compile-time variable object?

I was using constexpr recently but I just realized I used it wrong. I am curious whether I can create a compile-time variable (or variable object).
A constexpr definition from cppreference.com tells us:

The constexpr specifier declares that it is possible to evaluate the value of the function or variable at compile time.

So why is following code incorrect?

#include <iostream>

int main()
{
    constexpr int x = 30;
    x += 10;
    std::cout << x;
}

This integer can be perfectly evaluated in compile-time. I know compilers can optimize such a variable without constexpr modifier, but what if I want to have a compile-time object?

#include <iostream>

class ctFoo {
public:
    ctFoo()
        : value{ 0 }
    {
    }
    int accumulate(int value_) {
        return (value += value_), value;
    }
    int value;
};

int main()
{
    ctFoo foo;
    std::cout << foo.accumulate(100);
}

What certainty I have, that this code will be evaluated in compile-time? I am asking this, because I am currently writing some Vector2 and Vector3 maths and I wanted to create such implementation, that will be able to handle compile-time and run-time calculations. Is it even possible?
Thanks.

Edit

As max66 pointed out, constexpr implies const, but I am asking: why so? Modern compilers should be able to deduce its value in compile-time. Also, I know I can simply create another constexpr constant (ad. top-most code example), but my question refer to more complicated code.

Upvotes: 5

Views: 8207

Answers (2)

Qaz
Qaz

Reputation: 61970

So why is following code incorrect?

#include <iostream>

int main()
{
    constexpr int x = 30;
    x += 10;
    std::cout << x;
}

constexpr implies const. You need to constrain this to a constexpr context:

constexpr int foo() {
    int x = 30;
    x += 10;
    return x;
}

But what if I want to have a compile-time object?

#include <iostream>

class ctFoo {
public:
    ctFoo()
        : value{ 0 }
    {
    }
    int accumulate(int value_) {
        return (value += value_), value;
    }
    int value;
};

Give it constexpr support:

constexpr ctFoo() : value{ 0 }

constexpr int accumulate(int value_) {
    value += value_;
    return value;
}

The guarantees you now have are that if your ctFoo object is a constant expression and you call accumulate in a constexpr context like the foo function example, then you can use the result at compile-time. For example:

constexpr int foo() {
    ctFoo f;
    f.accumulate(10);
    return f.value;
}

static_assert(foo() == 10);

Or:

constexpr void accumulate(ctFoo& f) {
    f.accumulate(10);
}

constexpr int foo() {
    ctFoo f;
    accumulate(f);
    return f.value;
}

static_assert(foo() == 10);

The key thing to remember here is that runtime evaluation is also an option. If I set some ctFoo's value to a runtime value (e.g., user input), then there's no way an accumulate call could happen at compile-time. But that's okay - the same code works in both contexts.

Upvotes: 4

Rakete1111
Rakete1111

Reputation: 49018

You need to continue reading:

A constexpr specifier used in an object declaration implies const.

You cannot change a constexpr, that's the whole point of you being able to use it in constant expressions.

The next thing to consider is that you seem to be mixing two different contexts. I have no doubt that compilers will be able to optimize your last example completely away, but that is something else that what constexpr does. The keyword says that what it denotes can be evaluated at compile time, but it doesn't have to be. So you can write a constexpr function, with everything in it being possibly evaluated at compile time, and still have it work at run time.

The only time you can really have objects that can evaluated at compile time (so no optimizer) and that you can modify is when you are in a compile time context. You are in such a context for example in a constexpr function:

constexpr int foo() {
    int a = 10;
    ++a; // I can modify a.
    return a;
    // even if I did this: int Array[foo()];
}

But you have no such ability in a normal function, the language just doesn't allow it.

So, to answer your questions:

What certainty I have, that this code will be evaluated in compile-time?

You don't have any, because you aren't using constexpr. And even then, if you are calling the constexpr function somewhere that doesn't require a compile time evaluation, the function might get called at run time,

Is it even possible?

Sure, as said, you create a constexpr function, which will get evaluated at compile time if the need arises.

Upvotes: 1

Related Questions