![](/img/trans.png)
[英]Upcasting std::shared_ptr<Derived> to std::shared_ptr<Base> with templates
[英]How is it possible that std::shared_ptr<Derived> casts to std::shared_ptr<Base> with no compiler errors?
我正在嘗試實現自定義智能指針。 所以我有這樣的事情:
// Base class for every object
class Base {
public:
int n_holders {};
};
class Derived : public Base {};
// Custom shared pointer
template<class T>
class Sptr {
public:
T* obj;
Sptr(T* obj_) : obj{obj_} {}
};
void SomeFunc(Sptr<Base> obj) {}
void SomeFunc2(std::shared_ptr<Base> obj) {}
int main()
{
auto a = Sptr<Base>(new Base());
SomeFunc(a); // OK
auto b = Sptr<Derived>(new Derived());
SomeFunc(b); // Error. No matching function call to SomeFunc
auto c = std::shared_ptr<Base>(new Base());
SomeFunc2(c); // OK
auto d = std::shared_ptr<Derived>(new Derived());
SomeFunc2(d); // OK !!!
}
我的問題是,如果使用std::shared_ptr
沒有錯誤,為什么編譯器不能自動從Sptr<Derived>
轉換為Sptr<Base>
? 如何使它成為可能?
std::shared_ptr
有一個構造函數(來自cppreference ):
template< class Y >
shared_ptr( const shared_ptr<Y>& r ) noexcept; (9)
這個超載...
構造一個 shared_ptr,它共享由 r 管理的 object 的所有權。 如果 r 沒有管理 object,這也管理沒有 object。 如果 Y 不能隱式轉換為(C++17 前)與(C++17 起)T* 兼容,則模板重載不參與重載決議。
因此,在某種意義上,棘手的部分不是轉換共享指針,而是在指針類型不可隱式轉換時阻止它。 您可以使用 SFINAE 來實現這一點。
這是一個玩具示例,僅當T1
從T2
繼承時才啟用從Bar<T1>
到Bar<T2>
的轉換(但不是相反):
#include <type_traits>
template <typename T1>
struct Bar {
Bar() {}
template <typename T2, typename std::enable_if_t<std::is_base_of_v<T1,T2>,int> = 0>
Bar(Bar<T2>){}
};
struct Foo {};
struct Derived : Foo {};
int main(){
Bar<Derived> d;
Bar<Foo> b;
//d = b; // eror
b = d; // OK
}
您可能希望它更通用,例如共享指針,以便在T2*
可以轉換為T1*
時允許這種轉換,而不僅僅是當它們相互繼承時(參見std::is_convertible
,我不得不承認,我沒有t 真正理解 C++17 帶來的變化,所以我只能猜測:在這種情況下,可能是它的std::is_layout_compatible
)。 因此,要模仿 C++17 之前的智能指針,您可以使用:
template <typename T2, typename std::enable_if_t<std::is_convertible_v<T2*,T1*>,int> = 0>
Bar(Bar<T2>){}
這可以轉換所有T2
,其中T2*
可以轉換為T1*
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.