Принудительно компилировать функцию constexpr во время компиляции, даже если вычисление внутри содержит неконстантный массив, делая возвращенную константу obj? - PullRequest
0 голосов
/ 09 ноября 2019

Я прочитал о 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);
}

Ответы [ 2 ]

3 голосов
/ 09 ноября 2019

Из того, что я знаю, функция constexpr определенно оценивается во время компиляции, когда каждое значение внутри функции является const, а используемые функции являются constexpr, которые также используют только значения const. Кроме того, в другом посте говорилось, что можно принудительно компилировать время компиляции, сделав возвращенный объект obj const.

Все эти утверждения неверными. См. Ниже.


Нет, вызов функции constexpr гарантированно оценивается только во время компиляции, если она вызывается в контексте , требующем a (compile-время) постоянное выражение. Инициализатор 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, вообще не имеет значения.

0 голосов
/ 09 ноября 2019

от Microsoft:

Функция constexpr - это функция, возвращаемое значение которой может быть вычислено во время компиляции, когда этого требует код. Для использования кода требуется возвращаемое значение во время компиляции, например, чтобы инициализировать переменную constexpr или предоставить аргумент шаблона не-типа. Когда его аргументы являются значениями constexpr, функция constexpr создает константу времени компиляции. При вызове с аргументами non-constexpr или когда его значение не требуется во время компиляции, оно создает значение во время выполнения, как обычная функция. (Это двойное поведение избавляет вас от необходимости писать версии одной и той же функции constexpr и non-constexpr.)

, поэтому ваша функция вычисления будет оценивать время компиляции, если все параметры будут constexpr

...