我目前正在编写一些使用智能指针的代码,其中有必要在许多地方将这些指针强制转换为它们的基类型,并将它们作为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);
}
预期的用法是
void testMethod(const std::unique_ptr<Base>& ptr) {
// Do Stuff
}
auto ptr = std::make_unique<Derived>();
testMethod(FTScopedCast<Base>(ptr)());
删除符不带过,因为这样做会阻止向上转换。这样做也没有意义,因为无论如何都不会在创建的智能指针上调用删除器。
在堆上的分配被阻止,因为它可能允许包装器比它包装的指针活得更长,std::unique_ptr成员阻止了复制,标准的销毁顺序将确保原始指针在被销毁之前返回到原始智能指针,即使它在与包装器相同的作用域中声明。
我知道这不是线程安全的,但我认为在线程之间共享一个unique_ptr违反了它的单一所有者的契约。
如果我理解正确的话,意图是在函数调用期间"窃取"std::unique_ptr
的内容,然后在函数调用完成时将其返回给其原始所有者。
但这似乎是不必要的复杂。首先,正如@TheUndeadFish在评论中指出的那样,您可以将原始Base*
作为函数参数并使用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;
};
与此非常相似,std::observer_ptr
("世界上最愚蠢的智能指针")被提议用于c++ 17,但我不确定其状态