C++ unique_ptr 的 static_pointer_cast 的替代方案

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36120424/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 14:34:56  来源:igfitidea点击:

Alternatives of static_pointer_cast for unique_ptr

c++c++11castingshared-ptrunique-ptr

提问by skypHyman

I understand that using static_pointer_castwith unique_ptrwould lead to a shared ownership of the contained data.
In other terms, what I'd like to do is:

我知道使用static_pointer_castwithunique_ptr会导致所包含数据的共享所有权。
换句话说,我想做的是:

unique_ptr<Base> foo = fooFactory();
// do something for a while
unique_ptr<Derived> bar = static_unique_pointer_cast<Derived>(foo);

Anyway doing that results with two unique_ptrthat should never exist at the same time, so it is simply forbidden.
Right, it makes sense, absolutely, that's why there doesn't exist anything like static_unique_pointer_castindeed.

无论如何,这样做会导致两个unique_ptr永远不会同时存在的结果,所以这是完全禁止的。
是的,这是有道理的,绝对的,这就是为什么不存在任何类似的东西static_unique_pointer_cast

So far, in cases where I want to store pointers to those base classes, but I also need to cast them to some derived classes (as an example, imagine a scenario involving type erasure), I've used shared_ptrs because of what I've above mentioned.

到目前为止,如果我想存储指向这些基类的指针,但我还需要将它们强制转换为某些派生类(例如,想象一个涉及类型擦除的场景),我使用shared_ptrs 是因为我'上面提到过。

Anyway, I was guessing if there are alternatives to shared_ptrs for such a problem or if they are really the best solution in that case.

无论如何,我在猜测shared_ptr对于这样的问题是否有s 的替代方案,或者它们是否真的是这种情况下的最佳解决方案。

回答by Anedar

Raw pointers

原始指针

The solution for your problem is to get the raw (non-owning) pointer and cast it - then just let the raw pointer go out of scope and let the remaining unique_ptr<Base>constrol the lifetime of the owned object.

您的问题的解决方案是获取原始(非拥有)指针并将其unique_ptr<Base>强制转换- 然后让原始指针超出范围并让剩余的控制拥有对象的生命周期。

Like this:

像这样:

unique_ptr<Base> foo = fooFactory();

{
    Base* tempBase = foo.get();
    Derived* tempDerived = static_cast<Derived*>(tempBase);
} //tempBase and tempDerived go out of scope here, but foo remains -> no need to delete

Unique_pointer_cast

unique_pointer_cast

The other option is to use the release()function of unique_ptrto wrap it into another unique_ptr.

另一种选择是使用 的release()函数将unique_ptr其包装到另一个 unique_ptr 中。

Like this

像这样

template<typename TO, typename FROM>
unique_ptr<TO> static_unique_pointer_cast (unique_ptr<FROM>&& old){
    return unique_ptr<TO>{static_cast<TO*>(old.release())};
    //conversion: unique_ptr<FROM>->FROM*->TO*->unique_ptr<TO>
}

unique_ptr<Base> foo = fooFactory();

unique_ptr<Derived> foo2 = static_unique_pointer_cast<Derived>(std::move(foo));

Remember that this invalidates the old pointer foo

请记住,这会使旧指针无效 foo

Reference from raw pointers

来自原始指针的引用

Just for completeness of the answer, this solution was actually proposed as a small modification of the raw pointers by the OP in the comments.

只是为了答案的完整性,这个解决方案实际上是作为 OP 在评论中对原始指针的一个小修改而提出的。

Similar to using raw pointers one can cast the raw pointers and then create a reference out of them by derefering. In this case it is important to guarantee that the lifetime of the created reference does not exceed the lifetime of the unique_ptr.

与使用原始指针类似,可以转换原始指针,然后通过取消引用从它们中创建一个引用。在这种情况下,确保创建的引用的生命周期不超过 unique_ptr 的生命周期很重要。

Sample:

样本:

unique_ptr<Base> foo = fooFactory();
Derived& bar = *(static_cast<Derived*>(foo.get()));
//do not use bar after foo goes out of scope

回答by Jonathan Wakely

I understand that using static_pointer_cast with unique_ptr would lead to a shared ownership of the contained data.

我知道将 static_pointer_cast 与 unique_ptr 一起使用会导致所包含数据的共享所有权。

Only if you define it badly. The obvious solution would be for it to transfer ownership, so that the source object ends up empty.

只有当你定义不好时。显而易见的解决方案是让它转移所有权,这样源对象最终会变成空的。

If you don't want to transfer ownership then just use a raw pointer.

如果您不想转移所有权,则只需使用原始指针。

Or if you want two owners then use shared_ptr.

或者,如果您想要两个所有者,请使用shared_ptr.

It seems like your question is only partly about the actual cast operation, and partly just lack of a clear ownership policy for the pointer. If you need multiple owners, whether they both use the same type, or whether one is cast to a different type, then you should not be using unique_ptr.

似乎您的问题仅部分与实际的转换操作有关,部分只是缺乏明确的指针所有权政策。如果您需要多个所有者,无论他们都使用相同的类型,还是将一个转换为不同的类型,那么您不应该使用unique_ptr.

Anyway doing that results with two unique_ptr that should never exist at the same time, so it is simply forbidden.
Right, it makes sense, absolutely, that's why there doesn't exist anything like static_unique_pointer_cast indeed.

无论如何,这样做会导致两个不应该同时存在的 unique_ptr ,所以它只是被禁止的。
是的,这是有道理的,绝对的,这就是为什么确实不存在像 static_unique_pointer_cast 这样的东西。

No, that's not why it doesn't exist. It doesn't exist because it's trivial to write it yourself, if you need it (and as long as you give it sane semantics of unique ownership). Just get the pointer out with release()cast it, and put it in another unique_ptr. Simple and safe.

不,这不是它不存在的原因。它不存在,因为如果你需要它,你自己编写它是微不足道的(并且只要你给它独特所有权的理智语义)。只需使用release()cast取出指针,然后将其放入另一个unique_ptr. 简单又安全。

That isn't the case for the shared_ptr, where the "obvious" solution doesn't do the right thing:

情况并非如此shared_ptr,其中“明显”的解决方案没有做正确的事情:

shared_ptr<Derived> p2(static_cast<Derived*>(p1.get());

That would create two different shared_ptrobjects that own the same pointer, but don't share ownership (i.e. they would both try to delete it, causing undefined behaviour).

这将创建两个shared_ptr拥有相同指针的不同对象,但不共享所有权(即它们都会尝试删除它,导致未定义的行为)。

When shared_ptrwas first standardized there was no safe way to do that, so static_pointer_castand the related casting functions were defined. They needed access to the implementation details of the shared_ptrbookkeeping info to work.

shared_ptr第一次标准化时,没有安全的方法来做到这一点,因此static_pointer_cast定义了相关的转换函数。他们需要访问shared_ptr簿记信息的实施细节才能工作。

However, during the C++11 standardization process shared_ptrwas enhanced by the addition of the "aliasing constructor" which allows you to do the cast simply and safely:

但是,在 C++11 标准化过程中shared_ptr,通过添加“别名构造函数”得到了增强,它允许您简单安全地进行转换:

shared_ptr<Derived> p2(p1, static_cast<Derived*>(p1.get());

If this feature had always been part of shared_ptrthen it's possibly, maybe even likely, that static_pointer_castwould never have been defined.

如果此功能一直是其中的一部分,shared_ptr那么它可能static_pointer_cast永远不会被定义。

回答by Matthias

I would like to add something to the previous answer of Anedarwhich calls the release()member method of the given std::unique_ptr< U >. If one wants to implement a dynamic_pointer_castas well (in addition to a static_pointer_cast) for converting std::unique_ptr< U >to std::unique_ptr< T >, one must ensure the resource guarded by the unique pointer is released properly in case the dynamic_castfails (i.e. returns a nullptr). Otherwise, a memory leak occurs.

我想在Anedar的先前答案中添加一些内容,该答案调用release()给定std::unique_ptr< U >. 如果还想实现 a dynamic_pointer_cast(除了 a static_pointer_cast)以转换std::unique_ptr< U >std::unique_ptr< T >,则必须确保在dynamic_cast失败(即返回 a nullptr)的情况下正确释放由唯一指针保护的资源。否则,会发生内存泄漏。

Code:

代码

#include <iostream>
#include <memory>

template< typename T, typename U >
inline std::unique_ptr< T > dynamic_pointer_cast(std::unique_ptr< U > &&ptr) {
    U * const stored_ptr = ptr.release();
    T * const converted_stored_ptr = dynamic_cast< T * >(stored_ptr);
    if (converted_stored_ptr) {
        std::cout << "Cast did succeeded\n";
        return std::unique_ptr< T >(converted_stored_ptr);
    }
    else {
        std::cout << "Cast did not succeeded\n";
        ptr.reset(stored_ptr);
        return std::unique_ptr< T >();
    }
}

struct A { 
    virtual ~A() = default;
};
struct B : A {
    virtual ~B() { 
        std::cout << "B::~B\n"; 
    }
};
struct C : A {
    virtual ~C() { 
        std::cout << "C::~C\n"; 
    }
};
struct D { 
    virtual ~D() { 
        std::cout << "D::~D\n"; 
    }
};

int main() {

  std::unique_ptr< A > b(new B);
  std::unique_ptr< A > c(new C);
  std::unique_ptr< D > d(new D);

  std::unique_ptr< B > b1 = dynamic_pointer_cast< B, A >(std::move(b));
  std::unique_ptr< B > b2 = dynamic_pointer_cast< B, A >(std::move(c));
  std::unique_ptr< B > b3 = dynamic_pointer_cast< B, D >(std::move(d));
}

Output (possible ordering):

输出(可能的排序)

Cast did succeeded
Cast did not succeeded
Cast did not succeeded
B::~B
D::~D
C::~C

The destructors of Cand Dwill not be called, if one uses:

的析构函数C,并D不会被调用,如果一个用途:

template< typename T, typename U >
inline std::unique_ptr< T > dynamic_pointer_cast(std::unique_ptr< U > &&ptr) {
    return std::unique_ptr< T >(dynamic_cast< T * >(ptr.release()));
}