用c++11打造好用的any

現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時再通過向下轉型獲取實際數據。當轉型失敗時打印詳情。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章