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

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

 

posted on 2014-03-02 22:37  qicosmos(江南)  阅读(12820)  评论(2编辑  收藏  举报

导航