Из того, что я знаю, функция 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
, вообще не имеет значения.