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