Передача лямбды в функцию по ссылке

Есть вот такой пример:

#include <iostream>
#include <functional>

void qwer(const std::function<void()> &a)
{
        a();
}

int main()
{
        int x{0};

        auto f{
        [x]()mutable
        {
                std::cout << ++x << std::endl;
        }
        };

        qwer(f);
        qwer(f);
        qwer(f);

        return 0;
}

Выводит:

1
1
1

Есть 2 вопроса:

  1. Почему компилятор не выдает ошибку? Ведь функция qwer ждет, что ей передадут функцию, а мы ей передаем лямбду, которая, на сколько я знаю, не является функцией.
  2. Почему ,при передачи f в функцию qwer каждый раз создается копия f? Ведь при определении функции qwer при помощи & мы явно указали, что хотим использовать не копию объекта, а сам объект

Ответы (2 шт):

Автор решения: KoVadim
  1. std::function умеет работать с лямбдами. пруф

Class template std::function is a general-purpose polymorphic function wrapper. Instances of std::function can store, copy, and invoke any CopyConstructible Callable target -- functions, lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members.

  1. Если я правильно понимаю, там создается копия, потому что тип у f другой. Но достаточно сделать явный тип

    std::function<void()> f{
     [x]()mutable
     {
             std::cout << ++x << std::endl;
     }
     };
    

и все работает как нужно.

→ Ссылка
Автор решения: Павел Ериков

Объясню немного 2 вопрос.

Когда вы передаёте в функцию qwer лямбду, то т.к. она принимает аргумент типа function, то создается временный объект на который и ведет ссылка.

Вы можете удостоверится в этом добавив такую строчку в функцию qwer:

std::cout << &a << std::endl; //Вывести адрес

Сама лямбда у вас захватывает x по значению, по этому в каждом временном объекте своя копия x, нужно чтобы лямбда захватывала его по ссылке &x.

Поподробней и более умным языком написано тут.

То есть в вашем случае ссылка function<void()>& ведет на временный объект из-за различий в типах. А сама лямбда захватывает x по значению, а не по ссылке, по этому каждый временный объект работает с копией x.

Немного кода:

auto f{
    [&x]()mutable
    {
        std::cout << ++x << std::endl;
    }
};
→ Ссылка