有的時候咱們須要有一個萬能類型來進行一些操做,這時候boost::any就派上用場了。c++
boost::Any testInt(10); int val = static_cast<int>(testInt);
用法比較簡單,咱們來研究下boost::any是如何實現的。git
c++是一個強類型的語言,要實現一個萬能類型能夠考慮用void*來保存數據,而後用類型轉換進行操做,如:github
class MyAny{ MyAny(void* input):content_(input){ } template<typename T> T any_cast(){return *static_cast<T*>(content_)} private: void* content_; }
可是這樣的寫法有一個明顯的缺點就是類型不安全。安全
顯然咱們能夠用template來改進咱們的程序:單元測試
template<typename T> class MyAny{ MyAny(T input):content_(input){ } T any_cast(){return content_;} private: T content_; }
可是這樣咱們好像就沒有解決問題:vector<MyAny
爲了能寫下以下的代碼:測試
vector<MyAny> items; items.push_bacck(1); items.push_bacck(2.0);
咱們須要咱們的萬能類型有以下的行爲:this
咱們能夠經過添加一箇中間層來解決任何問題。
在boost::any中, 經過兩個中間層來達成咱們上面的目標, 類any做爲對外的接口層,承擔以模板做爲參數並提供必要的對外方法。
類placeholder做爲接口類,讓any使用。而holder是一個模板類做爲類placeholder的實現者, 這樣就避免的any對泛型參數的要求(能自動推到導出來)。
我這裏模仿了相關的實現,其代碼結構應該是這樣的:code
class Any { public: Any() :holder_(nullptr){} template<typename ValueType> Any(const ValueType& val) : holder_(new Holder<ValueType>(val)){ } private: IHolder* holder_; }; mb_interface IHolder{ } template<typename ValueType> class Holder : public IHolder{ public: Holder(const ValueType& val) : value_(val){ } } public: ValueType value_; }
其中Holder提供了具體的數據存儲服務,而 Any提供了對外的接口能力。
其中Holder必須提供兩個方法:接口
mb_interface IHolder{ virtual ~IHolder(){} virtual const std::type_info& type() const = 0; virtual IHolder* clone() const = 0; };
在 Any中, 提供瞭如下幾個個接口:
bool empty(){ return !holder_; } const std::type_info& type() const { return holder_ ? holder_->type() : typeid(void); } Any& operator=(Any rhs){ return swap(rhs); } template<typename ValueType> Any& operator=(const ValueType& val){ return Any(val).swap(*this); }
判斷是否爲空,查詢類型操做,賦值操做
固然必須還有最重要的any_cast操做,咱們看其實現:
template<typename ValueType> ValueType* anyCast(Any* val){ return (val && val->type() == typeid(ValueType)) ? &static_cast<Holder<ValueType>*>(val->getHolder())->value_ : 0; } template<typename ValueType> ValueType anyCast(Any& val){ ValueType* rtn = anyCast<ValueType>(&val); if (!rtn)boost::throw_exception(badAnyCast()); return *rtn; }
果真好簡單。呵呵~~~
最後添上單元測試,整個代碼就完善了:
mb::Any testInt(10); mb::Any testDouble(10.0); mb::Any testInt2(testInt); EXPECT_EQ(testInt.empty(), false); EXPECT_EQ(std::string(testDouble.type().name()), "double"); EXPECT_EQ(std::string(testInt2.type().name()), "int"); int val = mb::anyCast<int>(testInt); EXPECT_EQ(val, 10);