2

I wonder why std::function<...(...)> & needs to be specified as const when passed as an input parameter to a function. AFAIK there is no way to change it, right? Here is an example that compiles and runs fine. If I remove the const qualifier I get an error:

#include <iostream>
#include <functional>

//int foo(std::function<int(int)> &f)
int foo(const std::function<int(int)> &f)
{
    return f(6);
}
int main(int argc, char **argv)
{
    auto f1 = [=](int i){ if (i<5) {return 8*2;} else {return 2;} };
    auto f2 = [=](int i){ if (i>3) {return i*i;} else {return 2;} };
    std::cout << foo(f1) << "\n";
}

When I use the declaration without the const I get the following error:

main.cpp: In function ‘int main(int, char**)’:
main.cpp:13:21: error: cannot bind non-const lvalue reference of type ‘std::function<int(int)>&’ to an rvalue of type ‘std::function<int(int)>’
  std::cout << foo(f1) << "\n";
                     ^
In file included from /usr/include/c++/7/functional:58:0,
                 from main.cpp:2:
/usr/include/c++/7/bits/std_function.h:685:7: note:   after user-defined conversion: std::function<_Res(_ArgTypes ...)>::function(_Functor) [with _Functor = main(int, char**)::<lambda(int)>; <template-parameter-2-2> = void; <template-parameter-2-3> = void; _Res = int; _ArgTypes = {int}]
       function<_Res(_ArgTypes...)>::
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:4:5: note:   initializing argument 1 of ‘int foo(std::function<int(int)>&)’
 int foo(std::function<int(int)> &f)
     ^~~
OrenIshShalom
  • 5,974
  • 9
  • 37
  • 87
  • 1
    When you call `foo(f1)` from closure `f1` is created `function` instance as temporary value. Temporary value cannot be bound to *lvalue reference*. You can take function by value or const *lvalue reference*. – rafix07 Apr 18 '20 at 09:11
  • @rafix07 can you please explain how to do that? – OrenIshShalom Apr 18 '20 at 09:13
  • 2
    This is not specific to `std::function` – you can't bind any non-const reference to any temporary object. (Note that the type of your lambda is *not* `std::function` but a nameless unique type, which is why the message says "[...] after user-defined conversion [...]".) – molbdnilo Apr 18 '20 at 09:24

1 Answers1

3

A lambda is not a std::function, but a std::function can be created from a lambda. When you pass either f1 or f2 to foo(), the compiler must construct a temporary std::function object to give to the f parameter. However, an lvalue reference to a non-const object cannot be bound to a temporary object, exactly as the error message says.

To allow the passing of a temporary object, the f parameter must be changed to take the std::function by either:

  • value
  • lvalue reference to a const object
  • rvalue reference

Otherwise, you have to construct the std::function yourself in a variable, then pass that instead:

#include <iostream>
#include <functional>

//int foo(const std::function<int(int)> &f)
int foo(std::function<int(int)> &f)
{
    return f(6);
}

int main(int argc, char **argv)
{
    auto f1 = [=](int i){ if (i<5) {return 8*2;} else {return 2;} };
    auto f2 = [=](int i){ if (i>3) {return i*i;} else {return 2;} };
    std::function<int(int)> f = f1;
    std::cout << foo(f) << "\n";
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I understand the first two options, but not sure what is meant by rvalue reference. Can you please edit a short description of that? – OrenIshShalom Apr 18 '20 at 09:30
  • 1
    @OrenIshShalom see [rvalue](https://en.cppreference.com/w/cpp/language/value_category#rvalue) and [rvalue references](https://en.cppreference.com/w/cpp/language/reference#Rvalue_references). Lambdas and `std::function` were added in C++11, which is the same version that added rvalues, to support [move semantics](https://stackoverflow.com/questions/3106110/). So if you are going to use C++11 or later, it would be good to learn and understand what rvalues are and how to use them, they are a very important feature. – Remy Lebeau Apr 18 '20 at 09:33