c++11实现c++14的optional

时间:2023-12-31 19:45:14

  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 = ;
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, ));
mymap.insert(std::make_pair(c, )); auto it = mymap.find(a);
cout << it->second << endl;
}

可以看到用法和boost.optional的用法保持一致,实现起来也比较简单。

如果你觉得这篇文章对你有用,可以点一下推荐,谢谢。

c++11 boost技术交流群:296561497,欢迎大家来交流技术。