简体   繁体   English

unique_ptr的static_pointer_cast替代方案

[英]Alternatives of static_pointer_cast for unique_ptr

I understand that using static_pointer_cast with unique_ptr would lead to a shared ownership of the contained data. 我知道将static_pointer_castunique_ptr一起使用将导致所包含数据的共享所有权。
In other terms, what I'd like to do is: 换句话说,我想做的是:

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_ptr that should never exist at the same time, so it is simply forbidden. 无论如何,使用两个不应该同时存在的unique_ptr会导致结果,因此它被禁止。
Right, it makes sense, absolutely, that's why there doesn't exist anything like static_unique_pointer_cast indeed. 是的,绝对有道理,这就是为什么不存在像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_ptr s because of what I've above mentioned. 到目前为止,在我想存储指向这些基类的指针的情况下,我还需要将它们转换为某些派生类(例如,想象一下涉及类型擦除的场景),我使用了shared_ptr因为我上面提到的。

Anyway, I was guessing if there are alternatives to shared_ptr s for such a problem or if they are really the best solution in that case. 无论如何,我猜测是否有针对此类问题的shared_ptr的替代方案,或者在这种情况下它们是否真的是最佳解决方案。

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_ptr to wrap it into another unique_ptr. 另一种选择是使用unique_ptrrelease()函数将其包装到另一个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. 与使用原始指针类似,可以转换原始指针,然后通过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

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. 无论如何,使用两个不应该同时存在的unique_ptr会导致结果,因此它被禁止。
Right, it makes sense, absolutely, that's why there doesn't exist anything like static_unique_pointer_cast indeed. 是的,绝对有道理,这就是为什么不存在像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 . 只需使用release()指针,然后将其放入另一个unique_ptr Simple and safe. 简单安全。

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_ptr objects that own the same pointer, but don't share ownership (ie they would both try to delete it, causing undefined behaviour). 这将创建两个不同的shared_ptr对象,它们拥有相同的指针,但不共享所有权(即它们都会尝试删除它,导致未定义的行为)。

When shared_ptr was first standardized there was no safe way to do that, so static_pointer_cast and the related casting functions were defined. shared_ptr首次标准化时,没有安全的方法可以做到这一点,因此定义了static_pointer_cast和相关的转换函数。 They needed access to the implementation details of the shared_ptr bookkeeping info to work. 他们需要访问shared_ptr簿记信息的实现细节才能工作。

However, during the C++11 standardization process shared_ptr was 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_ptr then it's possibly, maybe even likely, that static_pointer_cast would never have been defined. 如果此功能始终是shared_ptr一部分,则可能甚至可能甚至不会定义static_pointer_cast

I would like to add something to the previous answer of Anedar which calls the release() member method of the given std::unique_ptr< U > . 我想在Anedar的前一个答案中添加一些东西,它调用给定std::unique_ptr< U >release()成员方法。 If one wants to implement a dynamic_pointer_cast as 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_cast fails (ie returns a nullptr ). 如果想要实现dynamic_pointer_cast (除了static_pointer_cast )以将std::unique_ptr< U >转换为std::unique_ptr< T > ,还必须确保唯一指针保护的资源被正确释放,以防万一dynamic_cast失败(即返回nullptr )。 Otherwise, a memory leak occurs. 否则,发生内存泄漏。

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 C and D will not be called, if one uses: 如果使用以下内容,则不会调用CD的析构函数:

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()));
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM