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

问题描述 投票:0回答:1

当我研究类继承和智能指针的一些方面时,我发现了一些我不理解的关于现代 C++ 类型转换的东西。我确定有一个合乎逻辑的解释,希望有人能提供。

考虑这些课程:

class base
{
public:
    virtual ~base() = default;
    void Func() const {}
};

class derived : public base
{
private:
    using base::Func; // makes base::Func inaccessible
};

class derived
中的“使用”使得通过
base::Func
访问
derived*
是不可能的,但是通过
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>
视为一个指向基的指针。

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) 编译器试图通过错误消息后的提示告诉我什么?

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

汉斯

c++ casting c++20 smart-pointers
1个回答
2
投票
reinterpret_cast<shared_ptr<base>&>(pShDer)->Func(); // ok

未定义的行为。您正在指示编译器将 glvalue 处理为

shared_ptr<derived>
就好像它是 glvalue 到
shared_ptr<base>
一样。通过与引用对象的实际类型不similar(即仅在
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
有一个构造函数,如果
std::shared_ptr<B>
可以隐式转换为
std::shared_ptr<D>
,则允许从
D*
构造一个
B*
。这是完全安全的。
shared_ptr
正好支持这个用例。当最后一个
shared_ptr
被销毁时,它总是会在构造原始
delete
的指针类型上调用
shared_ptr
。 (请注意,相比之下,
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
(或其他转换),有时很难判断它们是否安全。

© www.soinside.com 2019 - 2024. All rights reserved.