Почему std::bind предотвращает позднее связывание при использовании передачи по ссылке?

У меня есть базовый класс, производный класс и виртуальная функция-член. У меня также есть функция, которая берет ссылку на базовый класс и делает полиморфный вызов функции-члена:

#include <iostream>
#include <functional>
class Base
{
public:
    Base() {}
    virtual int getnum() { return 1; }
};

class Derived : public Base
{
public:
    Derived() {}
    virtual int getnum() { return 2; }
};

int getnumref(Base& b) { return b.getnum(); }

int main()
{
    Derived d;
    Base& bref = d;
    std::cout << getnumref(bref) << std::endl;
}

Здесь происходит позднее связывание, и вывод равен 2.

Но если я сейчас добавлю следующие строки в функцию main(), чтобы заранее определить аргумент функции, а затем вызвать ее:

std::function<int()> boundgetnumref = std::bind(getnumref, bref);
std::cout << boundgetnumref() << std::endl;

то вывод последней строки 1, т.е. здесь происходит раннее связывание, и вызывается функция-член базового класса.

Если я использую указатели, т.е.

//...
int getnumptr(Base* b) { return b->getnum(); }
//...
int main()
{
    Derived d;
    Base* bptr = &d;
    std::cout << getnumptr(bptr) << std::endl;
    std::function<int()> boundgetnumptr = std::bind(getnumptr, bptr);
    std::cout << boundgetnumptr() << std::endl;
}

тогда вывод обоих вызовов cout равен 2.

Почему происходит раннее связывание, когда я использую передачу по ссылке вместе с std::bind, а не иначе?


person andreasdr    schedule 28.09.2017    source источник
comment
Немного не связанно: не используйте std::bind в современном коде, всегда предпочитайте лямбда-выражения. См.: youtu.be/zt7ThwVfap0?t=1754.   -  person Vittorio Romeo    schedule 28.09.2017


Ответы (1)


std::bind хранит захваченные аргументы по значению, вызывая нарезку копии Derived в Base.

Если вы просто передадите std::reference_wrapper (указатель), который скопирует указатель, так что копирование нарезки не произойдет:

std::function<int()> boundgetnumref = std::bind(getnumref, std::ref(bref));

Однако предпочитайте лямбда-выражения, они являются лучшей практикой: их легче писать, читать и они более эффективны:

auto boundgetnumref = [&bref]() { return getnumref(breg); }
person Maxim Egorushkin    schedule 28.09.2017