通过使返回的 obj 常量,强制在编译时编译 constexpr 函数,即使内部计算包含非常量数组?

Force a constexpr function to be compiled at compile time, even if calculation inside contains a non-const array, by making the returned obj constant?

我阅读了 constexpr 函数以及它们何时进行编译时计算。现在我到了一个点,我必须用新值填充一个数组,因此数组不能是 const.

据我所知,constexpr 函数肯定是在编译时求值的,当函数内的每个值都是 const 并且使用的函数是 constexpr 时,它也只使用 const 值。此外,另一个 post 指出,可以通过使返回的 obj const.

强制编译时编译

比方说,我的函数是一个 constexpr,但内部有一个 non-const 数组,但返回的对象将是 const。我的函数 calculation 会在编译时被评估吗(强制?)。

template<int Size, typename T>
struct Array{
    T array[Size];
    Array(const T * a){
        for(int i = 0; i < Size; i++){
            array[i] = a[i];
        }
    }
};

template<typename T, int size>
class Example{

private:
    Array<size, T> _array;
    public:
        constexpr explicit Example(T * arr):_array(arr){};
        constexpr explicit Example(const T * arr):_array(arr){};
};

template<typename D, int size, typename ...buf, typename T>
constexpr auto calculations(const T & myObj){
    D test1[2];
    // calculation fills arr
    return Example<D, size>(test1);
}

int main(){
    const int size = 2;
    const double test1[size] = {1,2};
    const auto obj1 = Example<double, size>(test1); //compile time
    //obj2 calculations during compile time or run-time?
    const auto obj2 = calculations<double, size>(obj1);
}

来自微软:

A constexpr function is one whose return value can be computed at compile time when consuming code requires it. Consuming code requires the return value at compile time, for example, to initialize a constexpr variable or provide a non-type template argument. When its arguments are constexpr values, a constexpr function produces a compile-time constant. When called with non-constexpr arguments, or when its value isn't required at compile time, it produces a value at run time like a regular function. (This dual behavior saves you from having to write constexpr and non-constexpr versions of the same function.)

所以如果所有参数都是 constexpr,您的计算函数将评估编译时间

From what i know, a constexpr function is definitely evaluated at compile time, when every value inside the function is const and used functions are constexpr which also only use const values. Furthermore, an other post stated, that it's possible to force a compile time compilation by making the returned obj const.

所有这些说法都是错误的。见下文。


不,对 constexpr 函数的调用只有在上下文 需要 a(编译时) 常量表达式。 obj2 的初始化器不是这样的上下文,即使它是 const.

您可以通过将 obj2 声明为 constexpr 来强制对初始化程序进行编译时求值。 (然而,这与 const 的含义截然不同!)

即使那样它也不会起作用,因为 calculations<double, size>(obj1) 实际上不是常量表达式。 obj1 在没有声明 constexpr 的情况下也不是编译时常量。同样,这也不起作用,因为 test1 也不是常量表达式,也没有声明 constexpr

那么你还需要构造Arrayconstexpr的构造函数并且你需要在calculations中实际填充test1的值,因为访问未初始化的值会导致未定义的行为和未定义的行为使表达式不是常量表达式。

总而言之:

template<int Size, typename T>
struct Array{
    T array[Size];
    constexpr Array(const T * a) {
        for(int i = 0; i < Size; i++){
            array[i] = a[i];
        }
    }
};

template<typename T, int size>
class Example{

private:
    Array<size, T> _array;
    public:
        constexpr explicit Example(T * arr):_array(arr){};
        constexpr explicit Example(const T * arr):_array(arr){};
};

template<typename D, int size, typename ...buf, typename T>
constexpr auto calculations(const T & myObj){
    D test1[2];
    test1[0] = 0;
    test1[1] = 1;
    // calculation fills arr
    return Example<D, size>(test1);
}

int main(){
    const int size = 2;
    constexpr double test1[size] = {1,2};
    constexpr auto obj1 = Example<double, size>(test1); //compile time
    //obj2 calculations during compile time or run-time?
    constexpr auto obj2 = calculations<double, size>(obj1);
}

在 C++20 中,将有一个替代关键字 consteval,可以在函数上使用它代替 constexpr 以强制它 always在编译时进行评估。目前没有办法做到这一点,例如return 值的目的地 constexpr 变量。

事实上你的原始代码有未定义的行为。因为 Array 没有 constexpr 构造函数,所以永远不能在常量表达式中构造该类型的对象。并且因为 Example 使用该类型,所以它也不能用于常量表达式。这使得将 constexpr 放在其构造函数上是非法的,因为如果没有至少一组有效的模板参数和函数参数可以生成常量表达式,则声明为 constexpr 的函数会导致未定义的行为。 (同样适用于 calculations,因为它使用 Example.

所以你必须constexpr放在Array的构造函数上,如果你的程序应该是格式良好的。

内部常量表达式(例如calculations内部)创建的变量是否const根本不重要。