现any的关键技术
any能容纳所有类型的数据,因此当赋值给any时,需要将值的类型擦除才行,即以一种通用的方式保存所有类型的数据。这里可以通过继承去擦除类型,基类是不含模板参数的,派生类中才有模板参数,这个模板参数类型正是赋值的类型,在赋值时,将创建的派生类对象赋值给基类指针,基类的派生类中携带了数据类型,基类只是原始数据的一个占位符,通过多态,它擦除了原始数据类型,因此,任何数据类型都可以赋值给他,从而实现了能存放所有类型数据的目标。当取数据时需要向下转换成派生类型来获取原始数据,当转换失败时打印详情,并抛出异常。由于any赋值时需要创建一个派生类对象,所以还需要管理该对象的生命周期,这里用unique_ptr智能指针去管理对象的生命周期。
下面来看看一个完整的any是如何实现的吧。
struct Any{ template<typename U> Any(U && value) : m_ptr(new Derived < typename decay<U>::type> (forward<U>(value))), m_tpIndex(type_index(typeid(typename decay<U>::type))){} Any(void) : m_tpIndex(type_index(typeid(void) )){} Any(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) {} bool IsNull() const { return !bool(m_ptr); } template<class U> bool Is() const { return m_tpIndex == type_index(typeid(U)); } template<class U> typename decay<U>::type& AnyCast() { if (!Is<U>()) { cout << "can not cast " << typeid(U).name() << " to " << m_tpIndex.name() << endl; throw bad_cast(); } typedef typename decay<U>::type T; auto derived = static_cast<Derived<T>*> (m_ptr.get()); return derived->m_value; } template<class U> operator U() const { return AnyCast<typename decay<U>::type>(); } 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();//truestring 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时再通过向下转型获取实际数据。当转型失败时打印详情。
转载于:https://blog.51cto.com/minmn/1324054