Использование 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;
}
Другие вопросы по тегам