C++ - 我可以创建编译时变量对象吗?

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

我最近在使用 constexpr,但我才意识到我用错了。我很好奇我是否可以创建一个编译时变量(或变量对象)。
来自 cppreference.com 的 constexpr 定义告诉我们:

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

为什么下面的代码不正确?

#include <iostream>

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

这个整数可以在编译时完美求值。我知道编译器可以在没有 constexpr 修饰符的情况下优化这样的变量,但是如果我想要一个编译时对象怎么办?

#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);
}

我有什么把握,这段代码将在编译时求值? 我问这个,因为我目前正在写一些 Vector2 和 Vector3 数学,我想创建这样的实现,它将能够处理编译时和 运行-time 计算。有可能吗?
谢谢

编辑

正如 max66 指出的那样,constexpr 意味着 const,但我要问:为什么这样?现代编译器应该能够在编译时推断出它的价值。 另外,我知道我可以简单地创建另一个 constexpr 常量(广告。最上面的代码示例),但我的问题涉及更复杂的代码。

您需要继续阅读:

A constexpr specifier used in an object declaration implies const.

您无法更改 constexpr,这就是您能够在常量表达式中使用它的全部意义所在。

接下来要考虑的是您似乎混合了两种不同的上下文。我毫不怀疑编译器将能够完全优化您的最后一个示例,但那是 constexpr 所做的其他事情。关键字表示它所表示的 可以 在编译时进行评估,但不一定如此。因此,您可以编写一个 constexpr 函数,其中的所有内容都可能在编译时进行评估,并且仍然可以在 运行 时运行。

只有当您处于编译时上下文中时,您才能真正拥有可以在编译时评估的对象(因此没有优化器)并且您可以修改。例如,您在 constexpr 函数中处于这样的上下文中:

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

但是你在一个普通的函数中没有这样的能力,语言就是不允许。

所以,回答你的问题:

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

您没有,因为您没有使用 constexpr。即便如此,如果您在不需要编译时评估的地方调用 constexpr 函数,该函数可能会在 运行 时间被调用,

Is it even possible?

当然,如前所述,您创建了一个 constexpr 函数,如果需要,它将在编译时进行评估。

So why is following code incorrect?

#include <iostream>

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

constexpr 表示 const。您需要将其限制在 constexpr 上下文中:

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;
};

给它constexpr支持:

constexpr ctFoo() : value{ 0 }

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

您现在拥有的保证是,如果您的 ctFoo 对象是常量表达式,并且您在 constexpr 上下文中调用 accumulate,如 foo 函数示例,然后你可以在编译时使用结果。例如:

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

static_assert(foo() == 10);

或:

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

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

static_assert(foo() == 10);

这里要记住的关键是运行时评估也是一个选项。如果我将某些 ctFoovalue 设置为运行时值(例如,用户输入),那么 accumulate 调用不可能在编译时发生。但这没关系 - 相同的代码在两种情况下都有效。