簡體   English   中英

為什么 std::unique_ptr 不允許自己可復制?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM