问题
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?
回答1:
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*
回答2:
You can't pass &Foo(Apple)
when somebody may pass you a random Fruit
including a Pear
.
回答3:
It works but in opposite direction:
struct A {};
struct B: A {};
struct X {};
struct Y: X {};
static X useCallback(std::function<X(B)> callback) {
return callback({});
}
static Y cb(A) {
return {};
}
int main() {
useCallback(cb);
}
The signature of callback declares what will be passed to it and what is to be got back. Specific callback can take less specific types if doesn't care too much about them. Similarly it can return more specific type, extra information will be stripped. Refer to covariant vs contravariant types (input/output in simplified wording).
来源:https://stackoverflow.com/questions/26403556/converting-stdfunctionvoidderived-to-stdfunctionvoidbase