2

Question: How do I compile-time initialize a non-constant variable with a function while still enabling a runtime call to said function?

Details: I am using C++20, and I have the following code:

template<typename T>
constexpr auto diag(auto&&.. vals) {...}
// ...
constexpr auto M = diag<double>(1,2,3);

A expected, the above code compiles, diag is executed in compile-time, and M is constant. If I change the calling line to

auto M = diag<double>(1,2,3);

the code again, compiles, but this time, diag is executed in runtime, and M is not constant.

I would like to combine the above two: to have diag executed in compile time, but keep M mutable; basically to compile-time initialize a non-const variable with something complex, like the return value of diag.

Approach: I change the code to

template<typename T>
consteval auto diag(auto&&.. vals) {...}
// ...
auto M = diag<double>(1,2,3);

This time, M is non-constant, and diag is executed in compile-time, so effectively my goal is achieved. My problem is: consteval must be executed in compile-time, so if I want to use diag somewhere else in the code in runtime, I'll have to write another function.

This leads to the Question: How do I compile-time initialize a non-constant variable with a function while still enabling a runtime call to said function?

Note: In the above example code, diag creates a diagonal matrix with given entries. I want the code to be equivalent to

Matrix<double, 3, 3> M = { {1.0,0.0,0.0}, {0.0,2.0,0.0}, {0.0,0.0,3.0}};

Here, M is not a constant, but it is assigned from a constant, compile-time determined diagonal matrix.

Adelhart
  • 395
  • 2
  • 11
  • How did you determine that the function is executed at run-time when removeing `constexpr` from `M`? That sounds strange, since the variable assigned to shouldn't have an impact on if the function can be evaluated at compile-time or not. A `constexpr` function can do both, so it should be what you want. – super Jan 18 '21 at 12:57
  • 1
    Since you are using c++20, have you seen the new [constinit](https://en.cppreference.com/w/cpp/language/constinit) specifier? – super Jan 18 '21 at 12:59
  • @super I used [std::is_constant_evaluated](https://en.cppreference.com/w/cpp/types/is_constant_evaluated). And yes, I find it strange too. First I thought it depends on whether or not I have constexpr constructors, but it did not seem to make a change. – Adelhart Jan 18 '21 at 13:15
  • It would make your question easier to answer if you supply a [mcve] that demonstrates that. – super Jan 18 '21 at 13:17
  • @super With constinit I get the following error (gcc): ```constinit’ can only be applied to a variable with static or thread storage duration``` (I had ```constinit auto M = diag(2, 3, 4);``` in mí code). – Adelhart Jan 18 '21 at 13:20
  • This is why a [mcve] is good. The reason you are getting different result when adding/removing `constexpr` is not due to the fact that you are using `constexpr`, but that `constexpr` in some context imply `static`. You can most likely use a non-const variable with static storage duration and get it initialized at compile-time. – super Jan 18 '21 at 13:24

1 Answers1

4

You might use intermediate constexpr variable:

constexpr auto MInit = diag<double>(1,2,3);
auto M = MInit;

You might wrap it in lambda:

auto M = [](){ constexpr auto MInit = diag<double>(1,2,3); return MInit; }();
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • This looks like a good idea. Although, will it be as fast as the code without the intermediate variable? – Adelhart Jan 18 '21 at 13:36
  • @adel what code without the intermediate variable? – Yakk - Adam Nevraumont Jan 18 '21 at 13:37
  • @Yakk-AdamNevraumont I meant: does the addition of an intermediate variable make the code run slower? – Adelhart Jan 18 '21 at 13:39
  • @adel removing the intermediate variable makes the initialization occur at runtime, so no. If you want to compare it to some specific implementation without an intermediate variable, you'll have to say which one. Because there isn't a "exact same code that behaves the same without the intermediate variable" in evidence, which I think is what you are implying we should compare it to. – Yakk - Adam Nevraumont Jan 18 '21 at 14:00
  • @jarod42 I might make a global constexpr, like is_same_v, for this purpose. Cleaner at site of use. – Yakk - Adam Nevraumont Jan 18 '21 at 14:03
  • @Yakk-AdamNevraumont the version in which diag is marked ```consteval``` does work, and produces the same result as the version with the intermediate variable (both runtime and compile-time). Also, what do you mean with the global constexpr construction? Could you please write a short example code? – Adelhart Jan 18 '21 at 14:07
  • @Adelhart `template constexpr auto diag_v = diag(vals...);` -- doesn't work for every set of types. – Yakk - Adam Nevraumont Jan 18 '21 at 14:44
  • So that is the answer to my question -- compared to the `consteval` version. So now you test if it is as fast; good luck, because "copying" a double is going to be pretty ridiculously difficult to detect if it even happens. ;) – Yakk - Adam Nevraumont Jan 18 '21 at 14:45