Search code examples
c++pure-virtualstdbind

Bind pure virtual method


Suppose we have the following hierarchy:

class Add3Interface {
  virtual int add3 (const int&) const = 0;
};

class Add3: public Add3Interface {
  virtual int add3 (const int& arg) const override {
    return arg + 3;
  }
};

I want to bind the add3 method so I can use it in things like std::views::transform. The following code is fine:

const Add3 myAdder{};
const auto myFun = std::bind(&Add3::add3, myAdder, std::placeholder::_1);

However, in my use case, I don't have access to the concrete type, so I have to write something like this:

auto foo(Add3Interface& myAdder) {
    const auto myFun = std::bind(&Add3Interface::add3, myAdder,
                                 std::placeholders::_1);
    // ...
}

However, this makes the compiler upset:

/usr/include/c++/11/tuple:238:13: error: cannot declare field ‘std::_Head_base<0, Add3Interface, false>::_M_head_impl’ to be of abstract type ‘Add3Interface’

How can I bind my object's method?

I hoped something like typeid might help:

const auto myFun = std::bind(&(typeid(myAdder)::add3), myAdder, std::placeholders::_1);

But that only led to various syntax errors, which differed depending on how I positioned the various parentheses.

Naturally we could just use a lambda:

const auto myFun = [&myAdder] (const auto& arg) { return myAdder.add3(arg); };

But I'd prefer to use bind if possible, as I feel it represents what I am trying to do better from a semantic point of view.


Solution

  • std::bind attempts to store all of its arguments by-value. In this case, it is trying to copy myAdder into a data member of type Add3Interface. Of course, as Add3Interface is abstract, this fails. The issue is not with the member function pointer having the wrong type.

    If you want std::bind to refer to a stored argument by reference, pass a std::reference_wrapper<T> instead of a T. You can use the convenience functions std::ref or std::cref to construct std::reference_wrappers.

    auto foo(Add3Interface& myAdder) {
        const auto myFun = std::bind(&Add3Interface::add3, std::ref(myAdder),
                                     std::placeholders::_1);
        // ...
    }
    

    (To be precise, std::bind doesn't itself handle std::reference_wrapper specially at all. It will just store a copy of the std::reference_wrapper as a data member just like it stores a copy of any other argument. The std::reference_wrapper is correctly unwrapped at the time of the function call because the standard INVOKE operation that many standard utilities use to invoke callables unwraps it.)