0

Here I have the code

void foo(std::function<int(int)> stuff){
   //whatever
}

and it is called with

auto fct = [](int x){return 0;};

foo(fct);

Which works great. However, when I change foo to

void foo(std::function<int(int)>& stuff){ // only change is that it is passed by reference
   //whatever
}

The code doesn't compile. Why is this the case? I know we can just pass the object to a reference parameter directly, we don't need the & operator like for pointers. Why can't you pass std::function types by reference?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
k huang
  • 409
  • 3
  • 10
  • 2
    The parameters is a `function` type. The *lambda* will be used to construct a `function` temporary. The *temporary* can be pass-by-value, or pass-by-const-reference, but **not** pass-by-mutable-reference (because temporaries are not allowed to be mutated in general, to prevent bugs). – Eljay Jul 20 '22 at 21:48
  • `void foo(int &x);` `foo(42)` fails for a similar reason. `&` only accepts lvalues, and not only a lambda is an rvalue, it's type is also not `std::function`, so the result of converting it to a `std::function` (a temporary) is an rvalue regardless. Lambdas have unnamed types, and `std::function` can store any single callable object, which can be a lambda or a custom class or a function pointer, etc. – HolyBlackCat Jul 20 '22 at 21:49
  • @HolyBlackCat wait then what type is the lambda then? – k huang Jul 20 '22 at 21:51
  • Those types have no names. It's a class with an overloaded call operator (`operator()`). – HolyBlackCat Jul 20 '22 at 21:58
  • 1
    @khuang A lambda is an anonymous type generated by the compiler itself. A lambda is not a `std::function`, but a `std::function` can refer to and call a lambda. – Remy Lebeau Jul 20 '22 at 22:13
  • 1
    @RemyLebeau [`[expr.prim.lambda.closure]/1`](http://eel.is/c++draft/expr.prim.lambda#closure-1) *"unique, unnamed non-union class type"*. I've just tested on GCC, Clang, and MSVC and those names from the errors are rejected if you try to directly use them in code. Even if they worked, that would be a quirk of a specific compiler, not a standard requirement. – HolyBlackCat Jul 21 '22 at 06:54

1 Answers1

1

You are trying to bind a non-constant reference with a temporary object.

You could use a constant reference.

Here is a demonstration program.

#include <iostream>
#include <functional>

void foo( const std::function<int(int)> &stuff )
{
   int x = 10;

   std::cout << stuff( x ) << '\n';
}

int main()
{
    auto fct = [](int x){return x * 10;};

    foo(fct);
}

The program output is

100

Without the qualifier const you could write for example

#include <iostream>
#include <functional>

void foo( std::function<int(int)> &stuff )
{
   int x = 10;

   std::cout << stuff( x ) << '\n';
}

int main()
{
    auto fct = [](int x){return x * 10;};

    std::function<int(int)> f( fct );

    foo(f);
}

As for the lambda-expression then according to the C++ 17 Standard (8.1.5.1 Closure types)

1 The type of a lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type, called the closure type, whose properties are described below.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • I thought `fct` is an l-value, since it's a lambda assigned to a var? – k huang Jul 20 '22 at 21:52
  • 3
    @khuang The object of the type std::function that is created is not an lvalue. – Vlad from Moscow Jul 20 '22 at 21:54
  • `fct` is an l-value lambda, while the `std::function` generated by it is an r-value temporary. – Drew Dormann Jul 20 '22 at 21:55
  • I think I've misunderstood lambdas a bit. I thought they were of the type std::function. What type are lambdas then? – k huang Jul 20 '22 at 21:56
  • 1
    @khuang: You'd can't name their type, but you can use it. For example `template void foo(TLambda& stuff)` will work just fine (and optimize better) – Ben Voigt Jul 20 '22 at 21:57
  • 1
    @khuang _"...The lambda expression is a prvalue expression of unique unnamed non-union non-aggregate class type, known as closure type..."_ see https://en.cppreference.com/w/cpp/language/lambda – Richard Critten Jul 20 '22 at 21:58
  • 1
    @khuang From the C++ 14 STandard "3 The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed nonunion class type — called the closure type — whose properties are described below." – Vlad from Moscow Jul 20 '22 at 21:59