我了解了constexpr函数以及它们何时进行编译时计算。现在,我必须要用新值填充数组,因此数组不能为const

据我所知,当函数中的每个值都是constexpr且使用的函数是const(也仅使用constexpr值)时​​,肯定会在编译时评估const函数。此外,另一篇文章指出,可以通过使返回的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);
}

最佳答案

据我所知,当函数内部的每个值都是const并且使用的函数是constexpr(也只使用const值)时,肯定会在编译时评估constexpr函数。此外,另一篇文章指出,可以通过使返回的obj常量来强制进行编译时编译。


所有这些陈述都是错误的。见下文。



不可以,如果在需要(编译时)常量表达式的上下文中调用constexpr函数,则只能保证在编译时评估该调用。 obj2的初始化程序不是这样的上下文,即使它是const

通过将obj2声明为constexpr,可以强制对初始化程序进行编译时评估。 (然而,其含义与const完全不同!)

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

然后,您还需要制作Array constexpr的构造函数,并且实际上需要在test1内填充calculations的值,因为访问未初始化的值会导致未定义的行为,而未定义的行为会使表达式不是常量表达式。

总而言之:

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来强制其始终在编译时求值。目前,如果不进行例如返回值的目的地是constexpr变量。

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

因此,如果程序的格式正确,则无论如何都必须将constexpr放在Array的构造函数中。

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

09-26 01:07