1

This is follow-on from the post: Passing this pointer and arguments of class method to local lambda function at compile time.

Suppose that you want to use the result array of local constexpr lambda inside a constexpr method:

template<typename T, std::size_t N>
struct A {
    std::array<T, N> arr;
    constexpr A(std::array<T, N> arr) : arr(arr) {}
    constexpr auto operator+(A rhs) const {
        constexpr auto l = [](const auto& ta, const auto& ra) {
            std::array<T, N> result;
            result.fill(T{0});
            for (std::size_t i = 0; i < N; i++) {
                result[i] = ta[i] + ra[i];
            }
            return A(result);
        };
        constexpr auto result = l(arr, rhs.arr);
        /* use result by calling another constexpr methods / functions */
        return result;
    }
};

template<typename ... T>
A(T...) -> A<T..., sizeof...(T)>;

int main() {
    constexpr A a(std::array<int, 3>{1, 2, 3}), 
    constexpr A b(std::array<int ,3>{4, 5, 6});
    constexpr A c = a + b;
    return 0;
}

The compiler said that this and rhs are not constant expressions. How can I maintain constexpr-ness in this case?

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
apopa
  • 405
  • 2
  • 12

1 Answers1

0

TL;DR; Do not declare local variables constexpr in already constexpr functions.

Slight misunderstanding about how constexpr works.

constexpr functions are evaluated at compile-time only when used in compile-time context + as-if rule of course.

But defining result as constexpr means it must be constexpr even when the enclosing function is called in run-time context. That of course cannot be achieved, hence the error. One should use constexpr for local variables only if its "constness" is independent of the "constness" of the enclosing function.

constexpr in lambdas

This is little bit more tricky, see C++ usage of constexpr with lambda expression for more general details.

By binding the lambda into constexpr auto l, the initialiation of the lambda object must be constexpr, that is trivial and can only be broken by non-trivial capturing - e.g. by non-constexpr copy.

Lambdas' operator() are implicitly constexpr if possible. So there is no need to use [](const auto& ta, const auto& ra) constexpr {}, but it would result in a compiler error if the body could not be constexpr for some reason regardless of the outer function. This is similar version to making result constexpr.

Quimby
  • 17,735
  • 4
  • 35
  • 55
  • Ok, but there's a way to achieve this without lambda? Maybe with another constexpr method / function? @quimby – apopa Sep 01 '21 at 10:14
  • @apopa Sure, the lambda is not needed at all, [this](https://godbolt.org/z/dKEr8c7dv) works too. Not the `std::move`, you should use it because it will use move ctors of `T` to copy the arrays around. – Quimby Sep 01 '21 at 10:19
  • What if I want to use the result_array as an argument of a constexpr method? https://godbolt.org/z/4qca6YaKf – apopa Sep 01 '21 at 10:28
  • See the code in compile explorer above. Basically that array need to be returned from a constexpr function / method. @quimby – apopa Sep 01 '21 at 10:34
  • @apopa Then what you want is impossible. Image the function is run at run-time, the value would not be known at compile-time and the function must be compilable in both contexts. Perhaps you want `consteval`? – Quimby Sep 01 '21 at 10:39
  • Can you show me an example with consteval? @quimby – apopa Sep 01 '21 at 10:40
  • @apopa The goal is to use `zeros` in some `constexpr` context right? – Quimby Sep 01 '21 at 10:40
  • @apopa Hnnm, I tried but I cannot make it compile even with `consteval`, I do not have much experience in this aspect, perhaps try to ask a new question with the posted code from godbolt please? – Quimby Sep 01 '21 at 11:01
  • Yes, I'll do that, thank you for your time! :) @quimby – apopa Sep 01 '21 at 11:10
  • @apopa No problem :) Sorry for not getting you the right answer. – Quimby Sep 01 '21 at 11:34