consteval lambda with ignored parameter doesn't compile
Asked Answered
A

3

6

I have a lambda that ignores its int parameter and always returns a constant. If I mark it consteval, compilation fails because. The compiler complains about invoking the consteval lambda with a non-const parameter. But what does the parameter has to do with the lambda?

From CompilerExplorer:

source:3:16: error: the value of 'i' is not usable in a constant expression 5 | lambda(i);

void bar (auto lambda, int start, int end) {
    for (int i=start; i<end; ++i) {
        lambda(i);
    }
}

int main( )
{
    auto foo = [] (int) consteval { return 2;};

    bar(foo, 1, 9);

    return 0;
}
Airless answered 9/11, 2022 at 12:55 Comment(9)
I think c++ is doing the right thing here: saying that a parameter can be passed means that, at some point when you develop the code, it might have some logic depending on it. Allowing what you say would mean that, in the middle of development when you add such a logic, it'd suddenly stop working and turn your design upside down. So it's the principle of interfaced.Pastor
I would agree with you but... the code provided below by Jason Liam is working. I don't understand why.Airless
@Benedettoyou might want to add [language-lawyer] cos I am as confused as you.Lisettelisha
@Airless int& works because with int& it doesn't need to read the value.Gavel
Why do you have a lambda that takes a parameter that is never used?Chancroid
@Airless Please don't edit the question to include the answer. Readers who will read the question for the first time might think that the answer is just copy/paste of the question. It makes the answer obsolete and wastes user's time and effort in writing the answer. If you have a follow up question feel free to ask a new separate question.Gavel
You can also put const int& as well, in case you don't want a mutable reference.Adalbert
@JasonLiam I did not add the answer. I just pointed out a language dilemma. Why does it compiler with a reference?Airless
@Chancroid That is a long story. The question here is about language design and compilers implementations.Airless
G
2

One way to solve this(and the simplest) is to change the parameter type of the lambda to int& so that it doesn't need to read the value, as shown below:

int main( )
{//-------------------v------------------------->reference added
    auto foo = [] (int&) consteval { return 2;};

    bar(foo, 1, 9);

    return 0;
}

Working demo

Here is another contrived example that has similar behavior:

template<typename T>
consteval int func(const T) //note NO REFERENCE HERE
{
    return std::is_integral<T>::value;;
}

template<typename T>
//-----------------------v----->note the reference here
consteval int bar(const T&)
{
    return std::is_integral<T>::value;;
}

int main()
{
    
    int p = 2;
    //constexpr int d = func(p); //doesn't work
    constexpr int f = bar(p); //works

}

Contrived example demo

Gavel answered 9/11, 2022 at 13:3 Comment(3)
Wow. How come does this compile? Also taking into account what lorro says in his comment, the reference shouldn't change much.Airless
@Airless int& works because with int& it doesn't need to read the value.Gavel
sorry, I fail to understand your argument.Airless
C
0

You can also add an explicit check. Not the most elegant solution, but yeah:

#include <type_traits>

void bar(auto lambda, int start, int end) {
    for (int i = start; i < end; ++i) {
        if constexpr (std::is_invocable_v<decltype(lambda)>) {
            lambda();
        } else {
            lambda(i);
        }
    }
}

int main() {
    bar([] () consteval { return 2; }, 1, 9);
    bar([](int) { return 2; }, 1, 9);

    return 0;
}
Cailly answered 9/11, 2022 at 13:23 Comment(2)
This doesn't let you call a consteval lambda that has an unused parameter, which is what the OP is trying to do.Chancroid
@Chancroid Yes, but it might be an alternative.Cailly
H
0

Another way would be to make bar an immediate function. Altough, the usability of an immediate function returning void is rather limited.

consteval void bar(auto lambda, int start, int end) {
    for (int i = start; i < end; ++i) {
        lambda(i);
    }
}

int main() {
    bar([](int) consteval { return 2; }, 1, 9);
}
Highwrought answered 17/11, 2022 at 19:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.