First, I define two classes, which inherits from one another.
class A {
};
class B : public A {
};
Then, I declare a function that uses an std::function<void(A*)> :
void useCallback(std::function<void(A*)> myCallback);
Finally, I receive a std::function of a different (but theoretically compatible) type from somewhere else that I would like to use in my callback function:
std::function<void(B*)> thisIsAGivenFunction;
useCallback(thisIsAGivenFunction);
My compiler (clang++) refuses this because the type of thisIsAGivenFunction doesn't match the expected type. But with B inheriting from A, it would make sense for thisIsAGivenFunction to be acceptable.
Should it be? If not, why? And if it should, then what am I doing wrong?
Let's suppose that your class hierarchy is a little bigger:
struct A { int a; };
struct B : A { int b; };
struct C : A { int c; };
and you have functions like below:
void takeA(A* ptr)
{
ptr->a = 1;
}
void takeB(B* ptr)
{
ptr->b = 2;
}
Having that, we can say that takeA is callable with any instance of class derived from A (or A itself), and that takeB is callable with any instance of class B:
takeA(new A);
takeA(new B);
takeA(new C);
takeB(new B);
// takeB(new A); // error! can't convert from A* to B*
// takeB(new C); // error! can't convert from C* to B*
Now, what std::function is, it is a wrapper for callable objects. It doesn't care much about the signature of stored function object as long as that object is callable with parameters of its std::function wrapper:
std::function<void(A*)> a; // can store anything that is callable with A*
std::function<void(B*)> b; // can store anything that is callable with B*
What you are trying to do, is to convert std::function<void(B*)> to std::function<void(A*)>. In other words, you want to store callable object taking B* within wrapper class for functions taking A*. Is there an implicit conversion of A* to B*? No, there is not.
That is, one can as well call std::function<void(A*)> with a pointer to an instance of class C:
std::function<void(A*)> a = &takeA;
a(new C); // valid! C* is forwarded to takeA, takeA is callable with C*
If std::function<void(A*)> could wrap an instance of callable object taking only B*, how would you expect it to work with C*?:
std::function<void(B*)> b = &takeB;
std::function<void(A*)> a = b;
a(new C); // ooops, takeB tries to access ptr->b field, that C class doesn't have!
Fortunately, the above code does not compile.
However, doing this the opposite way is fine:
std::function<void(A*)> a = &takeA;
std::function<void(B*)> b = a;
b(new B); // ok, interface is narrowed to B*, but takeA is still callable with B*
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With