Ссылка на указатель на функцию

Вот так все работает отлично:

#include <iostream>
#include <functional>

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

void e()
{
        std::cout << "hello" << std::endl;
}

int main()
{
        //void (*ee)() = e;
        std::function<void()> ee = e;
        qwer(ee);
}

А вот так нет:

#include <iostream>
#include <functional>

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

void e()
{
        std::cout << "hello" << std::endl;
}

int main()
{
        void (*ee)() = e;
        //std::function<void()> ee = e;
        qwer(ee);
}

Выдает:

test2.cpp:18:7: error: cannot bind non-const lvalue reference of type ‘std::function<void()>&’ to an rvalue of type ‘std::function<void()>’
   18 |  qwer(ee);
      |       ^~
In file included from /usr/include/c++/9/functional:59,
                 from test2.cpp:2:
/usr/include/c++/9/bits/std_function.h:667:7: note:   after user-defined conversion: ‘std::function<_Res(_ArgTypes ...)>::function(_Functor) [with _Functor = void (*)(); <template-parameter-2-2> = void; <template-parameter-2-3> = void; _Res = void; _ArgTypes = {}]’
  667 |       function<_Res(_ArgTypes...)>::
      |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
test2.cpp:4:34: note:   initializing argument 1 of ‘void qwer(std::function<void()>&)’
    4 | void qwer(std::function<void()> &x)
      |           ~~~~~~~~~~~~~~~~~~~~~~~^

Почему он пишет, что ee это rvalue? Ведь это не так.


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

Автор решения: αλεχολυτ

В первом случае типы формального и фактического параметров функции совпадают с точностью до ссылочности. Т.е. функция принимает std::function<void()>&, а передаётся ей экземпляр типа std::function<void()>. В более общем случае T& и T соответственно. Очевидно, что такая привязка допустима, например, такое часто используется для модификации передаваемой в функцию переменной.

Во втором случае переменная имеет тип (указатель на функцию), отличный от std::function<void()>, но который может быть в него преобразован. При этом для обеспечения вызова функции создаётся временная переменная типа std::function<void()>. (Для фундаментальных типов можно привести пример с int и double, где целочисленная переменная может быть неявно преобразована в вещественную.) И в этот раз, чтобы привязка сработала для формального типа должна быть добавлена константность (т.к. язык запрещает привязку временных объектов к неконстантным ссылкам), либо следует убрать ссылочность. В данном случае переменная ee как раз порождает "rvalue" (подробнее о категориях выражений можно почитать здесь), т.е. временную безымянную переменную, для которой ожидается привязка к формальному аргументу x.

Возвращаясь к фундаментальным типам, можно рассмотреть такие варианты:

void f(double d);
void g(double& rd);
void h(const double& crd);

int i = 42;

f(i); // ok. локальная переменная d инициализируется значением i
g(i); // ошибка. int не может быть преобразован в ссылку на double
h(i); // ok. создаётся временный объект типа double и инициализируется значением i
→ Ссылка