Search code examples
c++lambdarvaluelvalue

Why do I get this lvalue & rvalue error while using a void function in C++?


I'm working on this exercise 18.7 in C++ Primer Plus (6th edition):

enter image description here

and the code I gave was like this, using the lambda expression as required:

#include <iostream>
#include <array>
#include <algorithm>

const int Size = 5;

template <typename T>
void sum(std::array<double, Size> a, T& fp);

int main()
{
    double total = 0.0;

    std::array<double, Size> temp_c = {32.1, 34.3, 37.8, 35.2, 34.7};
    sum(temp_c, [&total](double w){ total += w; });
    std::cout << "total: " << total << std::endl;
    std::cin.get();
    return 0;
}

template <typename T>
void sum(std::array<double, Size> a, T& fp)
{
    for (auto pt = a.begin(); pt != a.end(); ++pt)
        fp(*pt);
}

After I compiled it in VSCode, an error came out: cannot bind non-const lvalue reference of type 'main(int, char**)::<lambda(double)>&' to an rvalue of type 'main(int, char**)::<lambda(double)>'. I also checked the official answer, which is virtually the same as my code and gave the same error while being compiled. I think this is because the void type function doesn't match the T& type called in template function sum, but how to modify the code while keeping the original sum() function unchanged as required in the problem? And I'm also confused why there's a lvalue & rvalue problem here.

Thanks in advance for any answers & explanations.


Solution

  • By defining your lambda inline you have made it a temporary, an r-value. This means that because the template takes the function pointer as a non-const ref (ugh!), there's no conversion - the rvalue cannot be bound to an l-value parameter, so it does not compile. If you lift the lambda out into its own variable, it works:

    #include <iostream>
    #include <array>
    #include <algorithm>
    
    const int Size = 5;
    
    template <typename T>
    void sum(std::array<double, Size> a, T& fp);
    
    int main()
    {
        double total = 0.0;
    
        std::array<double, Size> temp_c = {32.1, 34.3, 37.8, 35.2, 34.7};
        auto l = [&total](double w){ total += w; };
        sum(temp_c, l);
        std::cout << "total: " << total << std::endl;
        std::cin.get();
        return 0;
    }
    
    template <typename T>
    void sum(std::array<double, Size> a, T& fp)
    {
        for (auto pt = a.begin(); pt != a.end(); ++pt)
            fp(*pt);
    }
    

    And even though the assignment explicitly says not to adapt sum(), if you turn the fp parameter into a const ref, the original temporary variable will compile too - and that would be a much more canonical C++ way of implementing this.