c++compiler-errorspolymorphismstd-functionstdbind

Using a vector of std::function<void(Base&)> to insert std::function<void(Derived&)>


I would like to use polymorphism to have a vector of std::function<void(Base&)> that take in a base class as a parameter, and fill the vector with std::function with a similar signature that instead take in a derived class as a parameter.

#include <functional>
#include <vector>
#include <stdio.h>

class Base
{
public:
    virtual ~Base() = default;
};

class Derived : public Base
{
public:
    Derived(int x, int y) : a(x), b(y) {}
    int a, b;
};

void DerivedFunction(Derived& d)
{
    printf("A: %d, B: %d\n", d.a, d.b);
}

using FunctionSignature = std::function<void(Base&)>; // Changing Base& to Derived& compiles fine.
static std::vector<FunctionSignature> myVector;

int main()
{
    FunctionSignature fn = [](Derived& d){ printf("A: %d, B: %d\n", d.a, d.b); };
    
    myVector.push_back(fn); // error
    myVector.push_back(std::forward<FunctionSignature>(fn)); // error
    myVector.push_back(std::bind(&DerivedFunction, std::placeholders::_1)); // error
    return 0;
}

What would be the correct way to push_back DerivedFunction into the vector?

Godbolt link: https://godbolt.org/z/b6Taqoqs8


Solution

  • A Derived& can be implicitly converted to a Base&. Inheritance models a is-a relation: Every Derived instance is-a Base. This relation is not mutual. You cannot implicitly convert a Base& to a Derived& (not every Base is-a Derived).

    You can use a dynamic_cast. It will throw an exception when the dynamic type of the object is not Derived:

    template <typename F>
    auto wrap(F f) {
        return [f](Base& b) { 
            f(dynamic_cast<Derived&>(b));
        };
    }
    
    int main() {
        FunctionSignature fn = wrap([](Derived& d){ printf("A: %d, B: %d\n", d.a, d.b); });
    }
    

    Live Demo

    You could catch the exception in the wrapper, or cast between pointers to get a nullptr rather than an exception (for details see here).

    As mentioned in a comment, the vector is a distraction. If the first line in main is fixed you can use the same with the vector.