首先,我定义了两个继承彼此的类。
class A {
};
class B : public A {
};
然后,我宣布一个使用std::function<void(A*)>
:
void useCallback(std::function<void(A*)> myCallback);
最后,我从其他地方收到一个std::function
不同(但理论上兼容)的类型,我想在回调函数中使用它:
std::function<void(B*)> thisIsAGivenFunction;
useCallback(thisIsAGivenFunction);
我的编译器(clang ++)拒绝这一点,因为thisIsAGivenFunction
的类型与预期的类型不匹配。但是B
继承自A
,thisIsAGivenFunction
可以接受。
应该吗?如果没有,为什么?如果它应该,那么我做错了什么?
答案 0 :(得分:15)
让我们假设您的类层次结构更大一些:
struct A { int a; };
struct B : A { int b; };
struct C : A { int c; };
你有以下功能:
void takeA(A* ptr)
{
ptr->a = 1;
}
void takeB(B* ptr)
{
ptr->b = 2;
}
有了这个,我们可以说takeA
是可调用的,其中任何类的实例都派生自A
(或A
本身),而takeB
使用类B
的任何实例<}> callable :
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*
现在,std::function
是什么,它是可调用对象的包装器。它并不关心存储的函数对象的签名,只要该对象是可调用的,其参数为std::function
包装器:
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*
您要做的是将std::function<void(B*)>
转换为std::function<void(A*)>
。换句话说,您希望将包含B*
的可调用对象存储在包含类A*
的函数中。是否存在A*
到B*
的隐式转换?不,没有。
也就是说,也可以使用指向类std::function<void(A*)>
实例的指针调用C
:
std::function<void(A*)> a = &takeA;
a(new C); // valid! C* is forwarded to takeA, takeA is callable with C*
如果std::function<void(A*)>
可以换行只调用B*
的可调用对象实例,那么您希望它如何与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!
幸运的是,上面的代码无法编译。
然而,相反的方式这样做很好:
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*
答案 1 :(得分:2)
当某人可能随机&Foo(Apple)
传递Fruit
时,您无法通过Pear
。
答案 2 :(得分:2)
它起作用但方向相反:
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);
}
回调的签名声明将传递给它的内容以及要返回的内容。如果不太关注它们,具体的回调可以采用不太具体的类型。同样,它可以返回更具体的类型,额外的信息将被剥离。请参阅协变与逆变类型(简化措辞中的输入/输出)。