• (原创)用c++11打造好用的any


      上一篇博文用c++11实现了variant,有童鞋说何不把any也实现一把,我正有此意,它的兄弟variant已经实现了,any也顺便打包实现了吧。其实boost.any已经挺好了,就是转换异常时,看不到详情,和boost.variant一样的问题。实现any比实现variant要简单,需要解决的关键技术是类型擦除,关于类型擦除我之前的博文有介绍,想了解的童鞋点这里

    实现any的关键技术

      any能容纳所有类型的数据,因此当赋值给any时,需要将值的类型擦除才行,即以一种通用的方式保存所有类型的数据。这里可以通过继承去擦除类型,基类是不含模板参数的,派生类中才有模板参数,这个模板参数类型正是赋值的类型,在赋值时,将创建的派生类对象赋值给基类指针,基类的派生类中携带了数据类型,基类只是原始数据的一个占位符,通过多态,它擦除了原始数据类型,因此,任何数据类型都可以赋值给他,从而实现了能存放所有类型数据的目标。当取数据时需要向下转换成派生类型来获取原始数据,当转换失败时打印详情,并抛出异常。由于any赋值时需要创建一个派生类对象,所以还需要管理该对象的生命周期,这里用unique_ptr智能指针去管理对象的生命周期。
      下面来看看一个完整的any是如何实现的吧。

    #include <iostream>
    #include <string>
    #include <memory>
    #include <typeindex>
    struct Any
    {
        Any(void) : m_tpIndex(std::type_index(typeid(void))){}
        Any(const Any& that) : m_ptr(that.Clone()), m_tpIndex(that.m_tpIndex) {}
        Any(Any && that) : m_ptr(std::move(that.m_ptr)), m_tpIndex(that.m_tpIndex) {}
    
        //创建智能指针时,对于一般的类型,通过std::decay来移除引用和cv符,从而获取原始类型
        template<typename U, class = typename std::enable_if<!std::is_same<typename std::decay<U>::type, Any>::value, U>::type> Any(U && value) : m_ptr(new Derived < typename std::decay<U>::type>(forward<U>(value))),
            m_tpIndex(type_index(typeid(typename std::decay<U>::type))){}
    
        bool IsNull() const { return !bool(m_ptr); }
    
        template<class U> bool Is() const
        {
            return m_tpIndex == type_index(typeid(U));
        }
    
        //将Any转换为实际的类型
        template<class U>
        U& AnyCast()
        {
            if (!Is<U>())
            {
                cout << "can not cast " << typeid(U).name() << " to " << m_tpIndex.name() << endl;
                throw bad_cast();
            }
    
            auto derived = dynamic_cast<Derived<U>*> (m_ptr.get());
            return derived->m_value;
        }
    
        Any& operator=(const Any& a)
        {
            if (m_ptr == a.m_ptr)
                return *this;
    
            m_ptr = a.Clone();
            m_tpIndex = a.m_tpIndex;
            return *this;
        }
    
    private:
        struct Base;
        typedef std::unique_ptr<Base> BasePtr;
    
        struct Base
        {
            virtual ~Base() {}
            virtual BasePtr Clone() const = 0;
        };
    
        template<typename T>
        struct Derived : Base
        {
            template<typename U>
            Derived(U && value) : m_value(forward<U>(value)) { }
    
            BasePtr Clone() const
            {
                return BasePtr(new Derived<T>(m_value));
            }
    
            T m_value;
        };
    
        BasePtr Clone() const
        {
            if (m_ptr != nullptr)
                return m_ptr->Clone();
    
            return nullptr;
        }
    
        BasePtr m_ptr;
        std::type_index m_tpIndex;
    };

    测试代码:

    void TestAny()
    {
        Any n;    
        auto r = n.IsNull();//true
        string s1 = "hello";
        n = s1;
        n = "world";
        n.AnyCast<int>(); //can not cast int to string
        Any n1 = 1;
        n1.Is<int>(); //true
    }

    再总结一下any的设计思路:Any内部维护了一个基类指针,通过基类指针擦除具体类型,any_cast时再通过向下转型获取实际数据。当转型失败时打印详情。

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

  • 相关阅读:
    Scala 获取当前时间
    mnist 数据集的识别源码解析
    tf.nn.softmax_cross_entropy_with_logits的用法
    softmax函数
    实现手写体 mnist 数据集的识别任务
    MNIST 数据集
    神经网络之激活函数
    模块化神经网络
    np.c_与np.r_
    学习率的选取-滑动平均
  • 原文地址:https://www.cnblogs.com/qicosmos/p/3420095.html
Copyright © 2020-2023  润新知