從Boost Variant談起
1 2 3
| #include <boost/variant.hpp> boost::variant<int, std::string> v; v = "Hello World!";
|
boost::get
使用boost::get
需要給出正確型別,不然會拋出 Exception
1 2
| std::cout << boost::get<std::string>(v) << std::endl; std::cout << boost::get<int>(v) << std::endl;
|
Use RTTI
1 2 3 4 5 6 7 8
| void var_print(const boost::variant<int, std::string> &v) { if (v.type() == typeid(int)) { std::cout << boost::get<int>(v) << std::endl; } else if (v.type() == typeid(std::string)) { std::cout << boost::get<std::string>(v) << std::endl; } }
|
每增加一種型別就要修改程式碼,並且影響性能
Visitor Pattern
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class var_visitor : public boost::static_visitor<void> { public: void operator()(int i) const { std::cout << i << std::endl; } void operator()(const std::string& str) const { std::cout << str << std::endl; } template <typename T> void operator()(T const &) const { std::cout << "FALLBACK: " << __PRETTY_FUNCTION__ << "\n"; } }; boost::apply_visitor(var_visitor(), v);
|
不過C++17的visit功能更強,實現更優雅
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class var_visitor { public void operator()(int i) const { std::cout << i << std::endl; } void operator()(const std::string& str) const { std::cout << str << std::endl; } template <typename T> void operator()(T const &) const { std::cout << "FALLBACK: " << __PRETTY_FUNCTION__ << "\n"; } }; visit(var_visitor(), v);
|
智逾期他的visit方式就先打住,有空在研究,介紹variant可以作些什麼
Stack-based run-time polymorphism
傳統基於heap based的polymorphism都是這麼做的
– 分配一塊記憶體
– 將物件創造於記憶體上
– 根據vtbl呼叫virtual function
– 歸還記憶體
所以一般都會寫出這樣的程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| struct Base { virtual ~Base() = default; virtual void func() const = 0; }; struct Der1 : public Base { void func() const override { cout << "Der1" << endl; } }; struct Der2 : public Base { void func() const override { cout << "Der2" << endl; } };
unique_ptr<Base> create(int v) { if (v) return make_unique<Der1>(); else return make_unique<Der2>(); }
auto test = [](const Base &obj) { obj.func(); }; auto obj = create(0); test(*obj);
|
而基於stack的作法,省去了最前面跟最後面的步驟,因此速度更快,如果有機會devirtualize的話,連vtbl都可以不需要
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| using derv = variant<Der1, Der2>; erv create(int v) { if (v) return Der1(); else return Der2(); }
template <typename BaseType, typename ... Types> BaseType& cast_to_base(variant<Types ...>& v) { return visit([](BaseType& arg) -> BaseType& { return arg; }, v); }
auto test = [](const Base &obj) { obj.func(); }; derv obj = create(0); test(cast_to_base<Base>(obj));
|
Reference
– variant in C++14
– 浅谈boost.variant的几种访问方式
– 让boost.variant支持lambda表达式访问
– default visitor function for boost::variant
– visiting variants using lambdas - part 1
– visiting variants using lambdas - part 2
– Polymorphism Polymorphism