Использование std::function для переноса объекта функции
Может кто-нибудь помочь мне понять, почему следующий код вызывает ошибку?
class A
{
public:
float& operator()()
{
return _f;
}
private:
float _f = 1;
} a;
auto& foo()
{
std::function<float()> func = a;
return func();
}
int main()
{
std::cout << foo() << std::endl;
}
Ошибка:
error: non-const lvalue reference to type 'float' cannot bind to a temporary of type 'float'
return func();
^~~~~~
1 error generated.
Здесь, в operator()
Я возвращаю ссылку на _f
и, следовательно, я подумал func()
не временный. Было бы здорово, если бы кто-то помог мне понять.
4 ответа
За std::function<float()> func
заявляешь func
как функтор, возвращающий float
не float&
, Как говорится в сообщении об ошибке, временный float
вернулся func()
не может быть привязан к неконстантной lvalue ссылке.
Вышеуказанная декларация не соответствует подписи A::operator()
который оборачивается. Но обратите внимание, что если изменить тип на std::function<float&()> func
соответствовать подписи A::operator()
ошибка компиляции может быть устранена, но тогда мы вернем ссылку, привязанную к локальной переменной, которая приводит к UB.
Обратите внимание, что для std::function<float()> func = a;
, std:: function инициализируется с копией a
, затем func()
вернет ссылку, привязанную к члену A
завернут в func
, которая является локальной переменной. И ссылка будет болтаться, когда выйти из функции foo
,
Как это исправить зависит от вашего дизайна, изменения auto& foo()
в auto foo()
Т.е. передача возвращаемого значения копией позволит избежать UB здесь.
Проблема не в использовании std::function
Это то, что вы пытаетесь вернуть временный float
от func()
в качестве ссылки. Это не сработает, поскольку объект перестанет существовать, как только закончится оператор.
Если вы измените auto& foo()
в auto foo()
он должен работать.
Я думаю, вы понимаете, что возвращение ссылки на локальную переменную недопустимо, когда переменная выходит из области видимости. То, что вы, кажется, упускаете, это то, что std::function<float()> func = a;
на самом деле создает местный std::function
от a
, Это не указывает на a
в любом случае, func
имеет свой A
, Что означает, что вызов func();
на самом деле не вызывает a.operator()
а скорее A
из func
, Затем мы возвращаемся к локальной переменной, возвращая ссылку, являющуюся злой частью.
Чтобы он скомпилировался, вы можете изменить подпись шаблона на float&()
но это все еще неопределенное поведение.
Исправление будет состоять в том, чтобы изменить тип возвращаемого значения вместо копии (на auto
), удалив ссылку.
Прочитав великие ответы выше, я попытался дать несколько разных мыслей.
Я думаю, что OP действительно хочет вернуть float&
определенного объекта (который является a
в примере ОП).
Так что, если ОП хочет foo
возвращать auto&
(который должен быть float&
), то это должно быть следующим, обратите внимание на std::bind
часть:
namespace T1
{
class A
{
public:
float& operator()()
{
std::cout << "a add = " << this << std::endl;
return _f;
}
float getF() { return _f; }
private:
float _f = 1;
} a;
auto& foo()
{
std::function<float&()> func = std::bind(&A::operator(), &a);
return func();
}
} // end of namespace T1
int main()
{
std::cout << "global a add = " << &(T1::a) << std::endl; // check a's address
float& f = T1::foo(); // note that `a`'s address is the same
std::cout << f << std::endl; // still 1
f = 777;
std::cout << f << std::endl; // now 777
std::cout << T1::a.getF() << std::endl; // it's 777
return 0;
}