在unique_ptr上有一个铸造操作员有危险吗?

Is it dangerous to have a cast operator on a unique_ptr?

本文关键字:操作员 有危险 有一个 unique ptr      更新时间:2023-10-16

我们有一个广泛的代码库,目前使用原始指针,我希望迁移到unique_ptr。 但是,许多函数期望原始指针作为参数,在这些情况下不能使用unique_ptr。 我意识到我可以使用 get() 方法来传递原始指针,但这会增加我必须接触的代码行数,而且我觉得它有点难看。 我滚动了自己的unique_ptr如下所示:

template <class T>
class my_unique_ptr: public unique_ptr <T>
{
  public:
    operator T*() { return get(); };
};

然后,每次我为需要原始指针的函数参数提供my_unique_ptr时,它都会自动将其转换为原始指针。

问题:这样做本身有什么危险吗? 我本以为这将是unique_ptr实现的一部分,所以我假设它的遗漏是故意的 - 有谁知道为什么?

式转换可能会意外发生很多丑陋的事情,例如:

std::unique_ptr<resource> grab_resource() 
{return std::unique_ptr<resource>(new resource());}
int main() {
    resource* ptr = grab_resource(); //compiles just fine, no problem
    ptr->thing(); //except the resource has been deallocated before this line
    return 0; //This program has undefined behavior.
}
这与在

unique_ptr<>上调用get相同,但它会自动完成。 您必须确保在函数返回后未存储/使用指针(因为unique_ptr<>在其生存期结束时将删除它)。

还要确保不要在原始指针上调用delete(即使是间接的)。

要确保的另一件事是,您不会创建另一个获取指针所有权的智能指针(例如另一个uniqe_ptr<>) - 请参阅上面的删除注释

unique_ptr<>没有自动为您执行转换(并明确调用get())的原因是为了确保您可以控制何时访问原始指针(以避免上述可能以静默方式发生的问题)

unique_ptr上提供隐式转换运算符(到原始指针)的主要"危险"源于这样一个事实,即unique_ptr应该对单所有权语义进行建模。 这也是为什么无法复制unique_ptr,但可以移动它。

考虑一下这种"危险"的例子:

class A {};
/* ... */
unique_ptr<A> a(new A);
A* a2 = a;
unique_ptr<A> a3(a2);

现在,两个unique_ptr模型 单一所有权语义 在所指向的对象上, 自由世界的命运 - 不,宇宙 - 悬而未决。

好吧,我有点戏剧化,但这就是想法。

就解决方法而言,我通常只会在指针上调用.get()并完成它,小心地认识到我刚刚got()的内容缺乏所有权。

鉴于您有一个大型的遗留代码库,您正在尝试使用 unique_ptr 迁移到该代码库,我认为您的转换包装器很好 - 但前提是您和您的同事将来在维护此代码时不会犯错误。 如果这是您认为可能的可能性(我通常会这样做,因为我偏执狂),我会尝试改造所有现有代码以显式调用.get(),而不是提供隐式转换。 编译器将很乐意为您找到需要进行此更改的所有实例。

在unique_ptr上有一个铸造操作员有危险吗?

已编辑 查看评论

---不,这当然不应该是危险的---: 事实上,该标准要求隐explicit转换为"安全布尔"(参见安全布尔习语)。

编辑说明:

新标准(也引入了unique_ptr)引入了explicit operator T()铸件。它仍然适用于unique_ptr,就好像它是一个隐式转换一样,在那个ifwhilefor(;x;)自动上下文转换为布尔值。

在我的辩护中,我的知识主要基于像 Boost 这样的 C++03 库,这些库确实定义了向隐unspecified-bool-type转换。

我希望这对其他人仍然有帮助。


(有关更多规范处理,包括转换为原始指针,请参阅其他答案)