[英]Why doesn't std::move() of unique_ptr from list<unique_ptr> really move it?
[英]Why doesn't std::unique_ptr allow itself to be copyable?
我知道这是为了防止 unique_ptr 的两个副本有一个可能的悬空指针,其中指向的对象可能已经被释放以及类似的东西。 但是他们为什么不决定允许一种深度复制,而不是仅仅复制指针,他们分配新内存并将复制委托给 unique_ptr 的模板参数类型。
主要问题是,如果它实际上是一个子类,那么只是盲目地用 new 调用模板参数的复制 ctor 将对对象进行切片,这通常使得这样做非常不安全。
也就是说,我发现扩展 unique_ptr 很有用:
template<class T, class D = std::default_delete<T>>
class autoclone_ptr : public std::unique_ptr<T, D> {
public:
autoclone_ptr() = default;
autoclone_ptr(autoclone_ptr &&) = default;
autoclone_ptr &operator=(autoclone_ptr &&) = default;
autoclone_ptr(const autoclone_ptr &a) : std::unique_ptr<T,D>(a ? a->clone() : nullptr) {}
autoclone_ptr &operator=(const autoclone_ptr &a) {
auto *t = a.get();
this->reset(t ? t->clone() : nullptr);
return *this; }
};
这只能为定义了clone()
方法的类实例化以避免切片问题。
复制指针时隐式进行深层复制会令人困惑。 通过使其既不能复制构造也不能复制可分配,您被迫做出有意识的决定。
为了调用复制构造函数,您必须使用 make_unique: 为什么 std::make_unique 调用复制构造函数。 它会将旧值复制到新内存中。
这个问题很好,其他答案没有提到一个更微妙的原因。
与评论相反,复制与专有所有权兼容。 如果T
是可复制的,假设设计可以允许复制unique_ptr<T>
。 堆分配的决定和允许复制的决定是不同的问题。 我将解决这个问题,他们为什么不使用那个假设的设计?
C++ 有一个怪癖。 当定义一个类模板Foo<T>
当且仅当T
可复制时,它应该是可复制的,它不能接受T
的不完整类型。 或者,如果接受不完整的类型,则可以撒谎并始终宣传可复制性,而不管T
。 但是,尝试复制可能是一个硬错误。 例如, std::vector<T>
总是宣传它是可复制的,以便它可以接受不完整的类型。
(解释:这是因为复制构造函数不能是模板,也没有返回值,所以 SFINAE 是不可能的。相反,可以为Foo<T>
引入基类,这些基类是根据T
的属性选择的。这意味着T
必须在Foo<T>
的实例化时完成。这可能不再适用于 C++20 和延迟约束检查。)
unique_ptr<T>
被设计为替代拥有的原始指针T*
。 毫无疑问, T
可能是不完整的。 因此,我们可以使unique_ptr<T>
始终不可复制,就像我们所做的那样,或者我们可以撒谎。 如果我们对此撒谎,那么默认情况下,包含unique_ptr<T>
的类现在也会撒谎。 在这种情况下, is_copy_constructible<T>
将变得不那么有用,因为存在多种类型。
这可以解决吗? 肯定有足够的工作。 我认为 C++20 可能有。 然而,我能想象到的任何解决方案都会对语言进行相当大的改变。 我认为这是它没有完成的最令人信服的原因。
正如一个答案所提到的,如果 (1) 指针指向基类并且 (2) 复制是天真地实现并且 (3) 没有保留比托管指针额外的状态,则切片可能会令人困惑。 有些用途切片不适用,因此人们可以就切片与可复制性的权衡进行真正的辩论。
(3) 不必如此。 首次构造指针时,我们可以隐藏一个函数,该函数会复制最派生的类型。 这将要求从指针到unique_ptr
的转换首先发生在最派生的类型上。 这是shared_ptr
的删除器采用的方法。 但是我们仍然不能正确地宣传我们的可复制性,所以现在复制变成了运行时错误。 这也使尺寸增加了一倍。
更有趣的是,(2)不一定是这种情况。 clone
是完整的样板文件,这意味着实现可以编写它。 但是,复制构造函数已经有意义,因此需要用户可以编写允许此操作的语法。 想象:
virtual T* clone() = default;
但这又回到了第一点。 如果用户需要编写一些东西, unique_ptr<T>
的可复制性需要以T
的属性为条件,如果我们还想支持不完整的类型,这是不可能的。
有一个针对indirect_value<T>
的提案P1950 ,它可能会或可能不会取得进展。 它解决了大多数这些问题。 它限制了复制构造函数,因此它可以正确地宣传可复制性,同时还支持不完整的类型。 它有条件地支持复制。 而且,它还传播 const 就好像它是一个值,而不是一个指针。 这使它成为决定堆分配的理想选择,没有其他包袱。
它不涉及切片,但有一个针对多态类型的兄弟提议。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.