堆分配的对象是否存在永不为空的唯一所有者?

Is there a never-null unique owner of heap allocated objects?

本文关键字:唯一 所有者 分配 对象 是否 存在      更新时间:2023-10-16

目前,我正在存储一个std::unique_ptr集合来堆多态类型的分配对象:

struct Foo {
virtual ~Foo() = default;
};
Collection<std::unique_ptr<Foo>> foos;

我需要的基本界面是将Foo的所有者放入/从foos.存储在foos中的对象永远不应该被nullptr所以我想用编译时检查替换运行时assert(owner_taken)。此外,我希望能够在可能预期可为空的上下文中使用非空所有者。

也许,我需要存储类似unique_ref的东西,但是我如何从foos中提取一个?我不想要副本,我想要存储的对象本身,所以owner->clone()不是解决方案。我也不能std::move(owner),因为"唯一引用"的状态之后将无效。

是否有一个干净的设计决策?

堆分配的对象是否存在永不为空的唯一所有者?

标准库中没有这种类型。

可以实现这种类型。只需定义一个带有unique_ptr成员的类型,并将默认构造函数标记为已删除。您也可以将构造函数标记为std::nullptr_t删除,以便在编译时阻止从nullptr构造。

无论是从外部指针构造,还是在构造函数中分配,都无法在运行时检查 null。

阅读您的问题,我解释以下要求:

  1. 您不想复制或移动对象本身 (Foo(
  2. 您不想要具有某种排除移动语义的空状态的包装器
  3. 对象本身(Foo(应该存储在堆上,使其生存期独立于控制流
  4. 对象本身(Foo(一旦不再使用就应该删除

由于构建/销毁,复制和移动是将对象移入/移出容器的唯一方法,唯一剩下的就是包装器对象,当移入/移出容器时会复制。

您可以自己创建此类对象,如下所示:

// LICENSE: MIT
#include <memory>
#include <utility>
template<typename T>
class shared_ref {
public:
template<typename ...Args>
shared_ref(Args&&... args)
: data{new T(std::forward<Args>(args)...)} 
{}
shared_ref(shared_ref&&) = delete;
shared_ref& operator=(shared_ref&&) = delete;
T& get() noexcept {
return *data;
}
const T& get() const noexcept {
return *data;
}
operator T&() noexcept {
return get();
}
operator const T&() const noexcept {
return get();
}
void swap(shared_ref& other) noexcept {
return data.swap(other);
}
private:
std::shared_ptr<T> data;
};
template<class T>
void swap(shared_ref<T>& lhs, shared_ref<T>& rhs) noexcept {
return lhs.swap(rhs);
}

我把它留给读者练习,以添加对基类的AllocatorDeleteroperator[]隐式转换构造器的支持。

然后可以按如下方式使用:

#include <iostream>
int main() {
shared_ref<int> r;  // default initialized
std::cout << "r=" << r << std::endl;
r = 5;  // type conversion operator to reference
std::cout << "r=" << r << std::endl;
shared_ref<int> s{7};  // initialized with forwarded arguments
std::cout << "s=" << s << std::endl;
std::swap(r, s);
std::cout << "r=" << r << ", " << "s=" << s << std::endl;
s = r;  // copy assignment operator
std::cout << "s=" << s << std::endl;
const shared_ref<int> t{s}; // copy constructor
std::cout << "t=" << t << std::endl;
//t = 8;  // const ref from a const object is immutable

return 0;
}