Search code examples
c++pass-by-referencestd-functionstdbind

Why does std::bind prevent late binding when using pass-by-reference?


I have a base class, a derived class, and a virtual member function. I also have a function which takes a base class reference and makes a polymorphic call to the member function:

#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;
}

Here, late binding occurs, and the output is 2.

But if I now add the following lines to the main() function in order to pre-define the argument to the function, and then call it:

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

then the output of the last line is 1, i.e. here, early binding occurs, and the member function of the base class is called.

If I use pointers, i.e.

//...
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;
}

then the output of both cout calls is 2.

Why does early binding take place when I use pass-by-reference together with std::bind, and not otherwise?


Solution

  • std::bind stores captured arguments by value causing a slicing copy of Derived to Base.

    If you just pass std::reference_wrapper (a pointer) that would copy the pointer, so that slicing copy does not happen:

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

    Prefer lambdas though, they are the best practice: easier to write, read and more efficient:

    auto boundgetnumref = [&bref]() { return getnumref(breg); }