c++14中將包含一個std::optional類,它的功能和用法和boost的optional類似。optional<T>內部存儲空間可能存儲了T類型的值也可能沒有存儲T類型的值,只有當optional被T初始化之后,這個optional才是有效的,否則是無效的,它實現了未初始化的概念。
optional的應用場景
函數返回無效對象
有時根據某個條件去查找對象時,如果查找不到對象時就會返回一個無效值,這不表明函數執行失敗,而是表明函數正確執行了,但是結果卻不是有用的值,這時就可以返回一個未初始化的optional對象出去,在外面判斷這個optional對象是否有效對象是否被初始化,如果沒有被初始化就表明這個值是無效的。
boost中的optional就實現了這種未初始化的概念,boost.optional的基本用法很簡單:
optional<int> op; if(op) cout<<*op<<endl; optional<int> op1 = 1; if(op1) cout<<*op1<<endl;
第一個op由於沒有被初始化,所以它是一個無效值,將不會輸出打印信息,第二個op被初始化為1,所以它是一個有效值,將會輸出1。optional經常用於函數返回值,像boost.property_tree中就有很多optional接口(關於boost.property_tree可以參考我前面博文的介紹:),比如get_child_optional接口,返回一個optional<ptree>對象,外面需要判斷它是否是一個有效值來確定是否取到了對應的子節點。
c++11實現optional
c++11中目前還沒有optional,在c++14中將會增加std::optional功能和用法和boost.optional類似。在c++14中的std::optional出來之前,如果不想依賴boost庫的話,就用c++11實現一個optional,也不是難事。
c++11實現optional需要注意的問題
1.內部存儲空間
由於optional<T>需要容納T的值,所以需要一個緩沖區保存這個T,這個緩沖區不能用普通的char數組,需要用內存對齊的緩沖區,這里還是采用std::aligned_storage,關於這個可以參考我前面的博文中對std::aligned_storage的討論。
2.拷貝構造函數和賦值構造函數
需要注意拷貝和賦值時,內部狀態和緩沖區銷毀的問題。內部狀態用來標示該optional是否被初始化,當已經初始化時需要先將緩沖區清理一下。需要增加右值版本優化效率。
來看看具體的實現吧:
#include <type_traits> template<typename T> class Optional { using data_t = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type; public: Optional() : m_hasInit(false) {} Optional(const T& v) { Create(v); } Optional(T&& v) : m_hasInit(false) { Create(std::move(v)); } ~Optional() { Destroy(); } Optional(const Optional& other) : m_hasInit(false) { if (other.IsInit()) Assign(other); } Optional(Optional&& other) : m_hasInit(false) { if (other.IsInit()) { Assign(std::move(other)); other.Destroy(); } } Optional& operator=(Optional &&other) { Assign(std::move(other)); return *this; } Optional& operator=(const Optional &other) { Assign(other); return *this; } template<class... Args> void emplace(Args&&... args) { Destroy(); Create(std::forward<Args>(args)...); } bool IsInit() const { return m_hasInit; } explicit operator bool() const { return IsInit(); } T& operator*() { return *((T*) (&m_data)); } T const& operator*() const { if (IsInit()) { return *((T*) (&m_data)); } throw std::exception(""); } bool operator == (const Optional<T>& rhs) const { return (!bool(*this)) != (!rhs) ? false : (!bool(*this) ? true : (*(*this)) == (*rhs)); } bool operator < (const Optional<T>& rhs) const { return !rhs ? false : (!bool(*this) ? true : (*(*this) < (*rhs))); } bool operator != (const Optional<T>& rhs) { return !(*this == (rhs)); } private: template<class... Args> void Create(Args&&... args) { new (&m_data) T(std::forward<Args> (args)...); m_hasInit = true; } void Destroy() { if (m_hasInit) { m_hasInit = false; ((T*) (&m_data))->~T(); } } void Assign(const Optional& other) { if (other.IsInit()) { Copy(other.m_data); m_hasInit = true; } else { Destroy(); } } void Assign(Optional&& other) { if (other.IsInit()) { Move(std::move(other.m_data)); m_hasInit = true; other.Destroy(); } else { Destroy(); } } void Move(data_t&& val) { Destroy(); new (&m_data) T(std::move(*((T*) (&val)))); } void Copy(const data_t& val) { Destroy(); new (&m_data) T(*((T*) (&val))); } private: bool m_hasInit; data_t m_data; };
測試代碼:
void TestOptional() { Optional<string> a("ok"); Optional<string> b("ok"); Optional<string> c("aa"); c = a; if (c<a) cout << "<" << endl; if (a == b) cout << "=" << endl; map<Optional<string>, int> mymap; mymap.insert(std::make_pair(a, 1)); mymap.insert(std::make_pair(c, 2)); auto it = mymap.find(a); cout << it->second << endl; }
可以看到用法和boost.optional的用法保持一致,實現起來也比較簡單。
如果你覺得這篇文章對你有用,可以點一下推薦,謝謝。
c++11 boost技術交流群:296561497,歡迎大家來交流技術。