![](/img/trans.png)
[英]Converting std::function<void(string, string)> to a generic std::function<void()>
[英]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.