目前,我正在存储一个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。
阅读您的问题,我解释以下要求:
- 您不想复制或移动对象本身 (
Foo
( - 您不想要具有某种排除移动语义的空状态的包装器
- 对象本身(
Foo
(应该存储在堆上,使其生存期独立于控制流 - 对象本身(
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);
}
我把它留给读者练习,以添加对基类的Allocator
、Deleter
、operator[]
、隐式转换构造器的支持。
然后可以按如下方式使用:
#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;
}