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,欢迎大家来交流技术。