![](/img/trans.png)
[英]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.