Search code examples
functionc++11bindstd-function

How can I use polymorphism with std::function?


Let's say I have 2 classes:

class A {}

class B : public A {}

And i want to use an std::function the receives anything of type A, but with assign to it methods that receive classes that inherit from A (like B).

void myFun(B bbbb) {}

std::function<void(A)> blah = std::bind(myFun, _1);

This obviously doesn't work, because the compiler won't just downcast implicitly.

But how can I do something like this ? Basically I want to hold a map of some basic std::function type, and in each mapped value it will hold an std::function to a derived type like B.

Is there a way to bind a cast operator to the placeholder ?


Solution

  • OK, well i've just done a workaround in the end.
    The compiler won't let you downcast implicitly, so I've binded a cast method.
    So, to keep it all generic and templated, it goes like this:

    First, a helper class to get the function argument type:

    template <typename T>
    class GetFunctionArgumentVal;
    
    template <class T, typename U >
    class GetFunctionArgumentVal<std::function<U(T)>>
    {
    public:
        typedef T arg;
        typedef U returnVal;
    };
    

    Then, a cast operator that casts using static_cast (keeps compile time type safety), then calls the function with the derived class:

    template <typename FUNCTION, typename BASE>
    void castAndCall(FUNCTION bf, BASE& temp) 
    {
        bf(static_cast< GetFunctionArgumentVal<FUNCTION>::arg >(temp));
    }
    

    Usage example:

    class A {};
    
    class B : public A {};
    
    class C : public A {};
    
    void targetB(B& temp) 
    {
    
    }
    
    void targetC(C& temp) 
    {
    
    }
    
        std::function<void(A &)> af;
        std::function<void(B &)> bf = targetB;
        std::function<void(C &)> cf = targetC;
    
        B b;
        C c;
    
        af = std::bind(castAndCall<decltype(bf),A>,bf,std::placeholders::_1);
        af(b);
    
        af = std::bind(castAndCall<decltype(cf),A>,cf,std::placeholders::_1);
        af(c);