簡體   English   中英

轉換std :: function <void(Derived*)> 到std :: function <void(Base*)>

[英]Converting std::function<void(Derived*)> to std::function<void(Base*)>

首先,我定義了兩個相互繼承的類。

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是有意義的。

應該是嗎? 如果沒有,為什么? 如果它應該,那么我做錯了什么?

我們假設您的類層次結構更大一些:

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類實例調用

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*的隱式轉換? 不,那里沒有。

也就是說,也可以使用指向C類實例的指針調用std::function<void(A*)>

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*

當有人可能隨機傳遞包括Pear在內的Fruit時,你不能通過&Foo(Apple)

它工作但方向相反:

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

回調的簽名聲明將傳遞給它的內容以及要返回的內容。 如果不太關心它們,具體的回調可以采用較少的特定類型。 同樣,它可以返回更具體的類型,額外的信息將被剝離。 請參閱協變與逆變類型(簡化措辭中的輸入/輸出)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM