作用域 std::unique_ptr 强制转换

tus*_*old 1 c++ pointers smart-pointers c++11

我目前正在使用智能指针编写一些代码,其中在许多点上有必要将这些指针强制转换为其基本类型并将它们作为 const 参数传递给函数。目前我正在使用shared_ptr和标准指针转换函数来实现这一点,但这似乎效率低下(因为每次转换至少需要一个CAS)并且还具有误导性(因为我们没有建模共享关系,父级是唯一的所有者)物体)。

因此,我想出了以下内容,但想检查它确实安全,还是有一些边缘情况会破坏它?

template <typename ToType, typename FromType>
class FTScopedCastWrapper {
public:
    explicit FTScopedCastWrapper(std::unique_ptr<FromType>& p) : from_ptr_(&p) {
        auto d = static_cast<ToType *>(p.release());
        to_ptr_ = std::unique_ptr<ToType>(d);
    }

    ~FTScopedCastWrapper() {
        auto d = static_cast<FromType *>(to_ptr_.release());
        (*from_ptr_) = std::unique_ptr<FromType>(d);
    }

    const std::unique_ptr<ToType>& operator()() {
        return to_ptr_;
    }


    // Prevent allocation on the heap
    void* operator new(size_t) = delete;
    void* operator new(size_t, void*) = delete;
    void* operator new[](size_t) = delete;
    void* operator new[](size_t, void*) = delete;

private:
    std::unique_ptr<FromType>* from_ptr_;
    std::unique_ptr<ToType> to_ptr_;
};

template <typename ToType, typename FromType>
FTScopedCastWrapper<ToType, FromType> FTScopedCast(std::unique_ptr<FromType>& p) {
    return FTScopedCastWrapper<ToType, FromType>(p);
}
Run Code Online (Sandbox Code Playgroud)

那么预期的用途是

void testMethod(const std::unique_ptr<Base>& ptr) {
    // Do Stuff
}

auto ptr = std::make_unique<Derived>();
testMethod(FTScopedCast<Base>(ptr)());
Run Code Online (Sandbox Code Playgroud)

删除器不会被传递,因为这样做会阻止向上转换。这样做也没有意义,因为无论如何都不会在创建的智能指针上调用删除器。

堆上的分配被阻止,因为它可能允许包装器比它所包装的指针寿命更长,std::unique_ptr 成员阻止复制,并且标准销毁顺序将确保原始指针在被销毁之前返回到原始智能指针,即使它是在与包装器相同的范围内声明的。

我知道这不是线程安全的,但我认为在线程之间共享 unique_ptr 违反了单个所有者的合同。

Tri*_*dle 5

如果我理解正确的话,其目的是在std::unique_ptr函数调用期间“窃取” a 的内容,然后在函数调用完成时将其返回给其原始所有者。

但这似乎是不必要的复杂。首先,正如 @TheUndeadFish 在评论中指出的那样,您可以将 rawBase*作为函数参数并使用 来调用它std::unique_ptr::get()。只要被调用的函数不做一些愚蠢的事情,比如调用delete传入的指针或将其存储在静态变量中以供以后使用,那么这就可以正常工作。

或者,如果您发现原始指针完全令人反感,您可以使用非拥有指针包装器,如下所示(未经测试,但您明白了):

template <typename T>
class unowned_ptr {
public:
    unowned_ptr() = default;
    unowned_ptr(const unowned_ptr&) = default;
    unowned_ptr& operator=(const unowned_ptr&) = default;

    template <typename U>
    unowned_ptr(const U* other) : ptr(other) {}

    template <typename U>
    unowned_ptr(const std::unique_ptr<U>& other) : ptr(other.get()) {}

    T* operator->() { return ptr; }
    const T* operator->() const { return ptr; }

private:
    T* ptr = nullptr;
};
Run Code Online (Sandbox Code Playgroud)

C++17 提出了与此非常相似的东西std::observer_ptr(“世界上最愚蠢的智能指针”),但我不确定状态。