zoukankan      html  css  js  c++  java
  • 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,欢迎大家来交流技术。

  • 相关阅读:
    windows下安装php5.5的redis扩展
    redis常见命令
    HDU 5869 Different GCD Subarray Query
    WA时查错点
    HDU 3333 Turing Tree
    HDU 5868 Different Circle Permutation
    AcWing 272 最长公共上升子序列 (dp)
    中国计量大学现代科技学院第四届“中竞杯”程序设计校赛 I 题 (双端队列bfs / 优先队列bfs)
    AtCoder ARC 109 D (拆点 + 分类讨论)
    codeforces 1408D. Searchlights (暴力 + 前缀优化)
  • 原文地址:https://www.cnblogs.com/qicosmos/p/3577449.html
Copyright © 2011-2022 走看看