Search code examples
c++vtablestdbinddynamic-dispatch

std::bind behaves differently when passed a pointer vs reference


The goal is to get a functor to the Base::Print.

#include <functional>
#include <iostream>

using namespace std;
class Base {
 public:
  virtual void Print() {
      cout << "Base" << endl;
  }

  virtual function<void()> get_fx1() {
    return bind(&Base::Print, this);
  }

  virtual function<void()> get_fx2() {
    return bind(&Base::Print, *this);
  }
};

class Derived : public Base {
 public:
  virtual void Print() override {
    std::cout << "Derived" << std::endl;
  }
};

int main()
{
  Derived d;
  Base *ptr = &d;
  ptr->Base::Print();           // prints Base (expecetd) #out1
  bind(&Base::Print, ptr)();    // prints Derived (expected Base) #out2
  ptr->get_fx1()();             // prints Derived (expected Base) #out3
  ptr->get_fx2()();             // prints Base (expected Derived because of out2 and out3) #out4

  cout << " -- " << endl;
  Derived d2;
  Base& ref = d2;
  ref.Base::Print();            // prints Base (expected) #out5
  bind(&Base::Print, ref)();    // prints Base (expected Derived because of out2 and out3. But this matches the behaviour with out4) #out6

  cout << " -- " << endl;
  (&ref)->Base::Print();        // prints Base (expected) #out7
  bind(&Base::Print, &ref)();   // prints Derived (I give up) #out8
  
  return 0;
}

I assumed that passing a pointer or a reference to std::bind is exactly the same. Clearly, I was wrong. It seems like when a reference is passed to std::bind you can prevent the vtable lookup. But when a pointer is passed to std::bind there is no way to prevent the vtable lookup. Empirically this is what it seems like.

So, the question is: does std::bind selectively skip the vtable lookup when a reference is passed?

I didn't find any reference only stating this.

Edit: Link to Compiler Explorer


Solution

  • std::bind() cannot bind "normal" references. You are slicing your derived class to your base class, and then calling the virtual function on the sliced object. For which the virtual call correctly calls the base class function.

    std::ref(...) can be used to bind functions that want references:

    How to bind function to an object by reference?