将std :: function <void(derived *)>转换为std :: function <void(base *)> </void(base *)> </void(derived *)>

时间:2014-10-16 11:45:04

标签: c++ c++11 std-function

首先,我定义了两个继承彼此的类。

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继承自AthisIsAGivenFunction可以接受。

应该吗?如果没有,为什么?如果它应该,那么我做错了什么?

3 个答案:

答案 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);
}

回调的签名声明将传递给它的内容以及要返回的内容。如果不太关注它们,具体的回调可以采用不太具体的类型。同样,它可以返回更具体的类型,额外的信息将被剥离。请参阅协变与逆变类型(简化措辞中的输入/输出)。