2016-03-03 49 views
1

考慮與不能直接存儲的成員,例如一類,因爲它不會有一個默認的構造函數,封裝類的構造函數沒有足夠的信息來創建它:間接成員RAII:unique_ptr還是可選的?

class Foo 
{ 
public: 
    Foo(){} // Default ctor 
private: 
    /* Won't build: no default ctor or way to call it's 
     non-default ctor at Foo's ctor. */ 
    Bar m_bar; 
}; 

顯然,m_bar需要以不同方式存儲,例如通過指針。一個std::unique_ptr似乎好一些,不過,因爲它會自動銷燬它:

std::unique_ptr<Bar> m_bar; 

它也可以使用std::experimental::optional,雖然:

std::experimenatl::optional<Bar> m_bar; 

我的問題是:1。什麼是權衡?和2.建立一個自動化他們之間選擇的類是否有意義?

具體來說,看着exception guarantees for the ctor of std::unique_ptrexception guarantees for the ctor of std::experimental::optional,它似乎很清楚,前一定要進行動態分配和釋放 - 運行速度不足,在某些(對齊的)內存緩衝,後者店的東西 - 尺寸方面的缺點。這些是唯一的折衷?

如果這確實是權衡,並鑑於這兩種類型的共享足夠的接口(構造函數,operator*)的,它是有意義的東西像

template<typename T> 
using indirect_raii = typename std::conditional< 
    // 20 - arbitrary constant 
    sizeof(std::experimental::optional<T>) > 
     20 + sizeof(std::exerimental::optional<T>)sizeof(std::unique_ptr<T>), 
    std::unique_ptr<T>, 
    std::experimental::optional<T>>::type; 

(注意它們之間自動進行選擇:有一個question discussing the tradeoffs between these two爲返回類型,但問題和答案專注於每一個傳遞的功能,這是不相關的,這些私有成員的用戶)

回答

1

IMO還有其他的權衡,在這裏打球。

  • unique_ptr不可複製或可複製分配,而optional是。
    我想你可以做的一件事就是讓indirect_RAII成爲一個類型並有條件地添加定義,以便通過調用Bar的拷貝來拷貝定義,即使當選擇unique_ptr時。 (或者相反,當它是一個可選項時禁用複製。)
  • optional類型可以有一個constexpr構造函數 - 在編譯時你無法真正做到與unique_ptr等價的東西。
  • Bar在構建unique_ptr<Bar>時可能不完整。當optional<Bar>已知時,它不可能不完整。在你的例子中,我猜你認爲Bar是完整的,因爲你採取了它的大小,但可能你可能想要實現一個類,使用indirect_RAII,如果情況並非如此。
  • 即使在Bar很大的情況下,您仍然可能會發現當選擇optional時,std::vector<Foo>將比unique_ptr的效果更好。我希望這發生在vector被填充一次的情況下,然後迭代多次。

也許,作爲一般的經驗法則,您的規則規則適用於您的程序常用,但我想對於「常用」,您選擇哪一個並不重要。使用indirect_RAII類型的另一種方法是,在每種情況下選擇一個或另一個,並在您將利用「通用接口」的地方,在需要時將該類型作爲模板參數傳遞。在性能關鍵領域,請手動進行適當的選擇。

+0

謝謝。對於你的第一點,我猜'shared_ptr'也是一個競爭者呢? –

+0

是的,我一開始也這麼認爲,但是語義從根本上發生了變化,對吧?由於它不復制底層數據。如果你只是用'const'類型來使用它,那麼我想它是可以的。但你不想如將另一個數據成員添加到您的類中,然後程序的某個其他部分突然從'optional'切換到'shared_ptr'並打破一些代碼。 –