Reputation: 739
I read about constexpr
functions and when they do a compile time calculation. Now i am at a point, where i have to fill an array with new values, thus the array cannot be const
.
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
.
Lets say, my function is a constexpr
, but has a non-const
array inside, but the returned object will be const
. Will my function calculation
be evaluated at compile time(forced?).
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);
}
Upvotes: 0
Views: 1279
Reputation: 22152
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.
All of these statements are wrong. See below.
No, a call to a constexpr
function is only guaranteed to be evaluated at compile-time if it is called in a context requiring a (compile-time) constant expression. The initializer of obj2
is not such a context, even if it is const
.
You can force the initializer to be compile-time evaluated by declaring obj2
as constexpr
. (Which however has very different meaning than const
!)
Even then it is not going to work, because calculations<double, size>(obj1)
is not actually a constant expression. obj1
is not a compile-time constant without declaring it constexpr
as well. Similarly this doesn't work because test1
is not a constant expression without declaring it constexpr
as well.
Then you also need to make the constructor of Array
constexpr
and you need to actually fill the values of test1
inside calculations
, because accessing uninitialized values causes undefined behavior and undefined behavior makes expressions not constant expressions.
So all in all:
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);
}
In C++20 there will be an alternative keyword consteval
which one can use instead of constexpr
on a function to force it to always be evaluated at compile-time. Currently there is no way to do that without making e.g. the destination of the return value a constexpr
variable.
In fact your original code has undefined behavior. Because Array
does not have a constexpr
constructor, objects of that type can never be constructed in constant expressions. And because Example
uses that type, it cannot be used in constant expressions either. This makes it illegal to put constexpr
on its constructor, because a function declared constexpr
causes undefined behavior if there isn't at least one valid set of template arguments and function arguments that would produce a constant expression. (The same then applies to calculations
as well, because it uses Example
.
So you must put constexpr
on the constructor of Array
in any case if your program is supposed to be well-formed.
Whether variables created inside the constant expression (e.g. inside calculations
) are const
does not matter at all.
Upvotes: 3
Reputation: 650
from Microsoft:
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.)
so your calculate function will evaluated compile time if all parameters are constexpr
Upvotes: 1