使用智能指针了解C++类型转换



当我玩类继承和智能指针的一些侧面时,我发现了一些我不理解的现代C++类型转换。我相信有一个合乎逻辑的解释,希望有人可以提供它。

请考虑以下类:

class base
{
public:
virtual ~base() = default;
void Func() const {}
};
class derived : public base
{
private:
using base::Func; // makes base::Func inaccessible
};

class derived中的"使用"使得通过derived*访问base::Func变得不可能,但通过base*仍然可以访问该功能。

现在,智能指针开始发挥作用。当然,base:Func具有相同的可访问性规则。

using namespace std;
shared_ptr<derived> pShDer{ make_shared<derived>() };
// error C2248: 'derived::Func': cannot access private member declared in class 'derived'
//pShDer->Func();
((shared_ptr<base>&)pShDer)->Func(); // ok
// error C2440: 'static_cast': cannot convert from 'std::shared_ptr<derived>' to 'std::shared_ptr<base> &'
//static_cast<shared_ptr<base>&>(pShDer)->Func();
reinterpret_cast<shared_ptr<base>&>(pShDer)->Func(); // ok
static_cast<shared_ptr<base>const &>(pShDer)->Func(); // ok
reinterpret_cast<shared_ptr<base>const &>(pShDer)->Func(); // ok

这个想法是,我不构造一个临时shared_ptr<base>并在代码行结束时销毁它,而是我希望编译器将现有shared_ptr<derived>视为指向 base 的指针。

unique_ptr一起玩游戏会稍微改变情况:

unique_ptr<derived> pUnDer{ make_unique<derived>() };
// error C2248: 'derived::Func': cannot access private member declared in class 'derived'
//pUnDer->Func();
((unique_ptr<base>&)pUnDer)->Func(); // ok
// error C2440: 'static_cast': cannot convert from 'std::unique_ptr<derived,std::default_delete<derived>>' to 'std::unique_ptr<base,std::default_delete<base>> &'
// static_cast and safe_cast to reference can only be used for valid initializations or for lvalue casts between related classes
//static_cast<unique_ptr<base>&>(pUnDer)->Func();
reinterpret_cast<unique_ptr<base>&>(pUnDer)->Func(); // ok
// error C2440: 'static_cast': cannot convert from 'std::unique_ptr<derived,std::default_delete<derived>>' to 'const std::unique_ptr<base,std::default_delete<base>> &'
// Reason: cannot convert from 'std::unique_ptr<derived,std::default_delete<derived>>' to 'const std::unique_ptr<base,std::default_delete<base>>'
// No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
//static_cast<unique_ptr<base>const&>(pUnDer)->Func();
reinterpret_cast<unique_ptr<base>const&>(pUnDer)->Func(); // ok

显然,C型演员总是有效,reinterpret_cast也是如此。但是为什么static_case只在一种情况下编译(而不是在所有情况下或没有)?(Microsoft)编译器试图通过错误消息后面的提示告诉我什么?

这背后没有用例,只是一个想要了解的人:-)谢谢!

汉斯

reinterpret_cast<shared_ptr<base>&>(pShDer)->Func(); // ok

未定义的行为。您正在指示编译器将 glvalue 视为shared_ptr<derived>的 glvalue 视为 glvalue toshared_ptr<base>。通过与引用对象的实际类型不相似(即仅在const限定条件上有所不同)进行的成员访问会导致未定义的行为。

reinterpret_cast<shared_ptr<base>const &>(pShDer)->Func(); // ok

出于与上述相同的原因,也未定义的行为。

((shared_ptr<base>&)pShDer)->Func(); // ok

也是未定义的行为,因为它等效于reinterpret_cast<shared_ptr<base>&>(pShDer)->Func();.

static_cast<shared_ptr<base>const &>(pShDer)->Func(); // ok

这个还可以。它创建一个临时shared_ptr<base>对象,通过构造函数调用初始化,static_cast<shared_ptr<base>const &>(pShDer)引用此临时对象。

std::shared_ptr有一个构造函数,如果D*可以隐式转换为B*,则允许从std::shared_ptr<D>构造std::shared_ptr<B>。这是完全安全的。shared_ptr完全支持此用例。销毁最后一个shared_ptr时,它将始终在构造原始shared_ptr时使用的指针类型上调用delete。(请注意,相比之下,unique_ptr通过unique_ptr实例的实际删除程序类型进行删除。

//static_cast<shared_ptr<base>&>(pShDer)->Func();

失败,因为不允许非const左值引用绑定到临时对象。因此,static_cast也指定不允许等效转换为引用类型。

这个想法是,我不构造一个临时shared_ptr并在代码行结束时销毁它,

static_cast的情况下,你正是这样做的,这是一件好事。

而是我希望编译器将现有shared_ptr视为指向 base 的指针。

这基本上是不允许的,并且将始终具有UB。

((unique_ptr<base>&)pUnDer)->Func(); // ok
reinterpret_cast<unique_ptr<base>&>(pUnDer)->Func(); // ok
reinterpret_cast<unique_ptr<base>const&>(pUnDer)->Func(); // ok

所有这些未定义的行为都与shared_ptr相同的原因。

//static_cast<unique_ptr<base>&>(pUnDer)->Func();

由于与shared_ptr相同的原因不起作用。

//static_cast<unique_ptr<base>const&>(pUnDer)->Func();

不起作用,因为您正在尝试复制unique_ptr(通过创建临时对象)。unique_ptr是不可复制的。

但是,它是可移动的,因此static_cast<unique_ptr<base>const&>(std::move(pUnDer))->Func();可以工作,但会导致所有权转移到临时unique_ptr实例,然后在完整表达式结束时销毁该实例,并随之销毁托管derived对象。如果您的base没有virtual析构函数,这也将具有 UB。


总而言之:如果您不是 100% 确定您了解它在特定用例中的作用,请不要使用reinterpret_cast。否则会导致 UB。切勿使用 C 型石膏,因为它们有时可能会解析为reinterpret_cast(或其他石膏),有时很难判断它们是否安全。

相关内容

  • 没有找到相关文章

最新更新