id _ to _ type 返回的是某个 id 对应的类别实例,所以还必须 decltype 来计算类型。这样我们就可以按照 T 来获取一个 tuple 类型了,接下来是要将 T 的值赋给 tuple,然后就可以按照索引来访问 T 的字段了。
对于 clang 编译器,pod 结构体是可以直接转换为 std::tuple 的,所以对于 clang 编译器来说,到这一步就结束了。
template <std::size_t i,="" class="" t="">decltype(auto) get(const T& val) noexcept { auto t = reinterpret_cast<const decltype(detail::as_tuple<t="">())*>( std::addressof(val) ); return get<i>(*t);}</i></const></std::size_t>
然而,对于其它编译器,如 msvc 或者 gcc,tuple 的存储并不是连续的,不能直接将 T 转换为 tuple,所以更通用的做法是先做一个内存连续的 tuple,然后就可以将 T 直接转化为 tuple 了。
下面是实现存储连续的 tuple 代码:
template <std::size_t n,="" class="" t="">struct base_from_member { T value;};template <class i,="" class="" ...tail="">struct tuple_base;template <std::size_t... i,="" class="" ...tail="">struct tuple_base< std::index_sequence<i...>, Tail... > : base_from_member<i ,="" tail="">...{ static constexpr std::size_t size_v = sizeof...(I); constexpr tuple_base() noexcept = default; constexpr tuple_base(tuple_base&&) noexcept = default; constexpr tuple_base(const tuple_base&) noexcept = default; constexpr tuple_base(Tail... v) noexcept : base_from_member<i, tail="">{ v }... {}};template <>struct tuple_base<std::index_sequence<> > { static constexpr std::size_t size_v = 0;};template <class ...values="">struct tuple: tuple_base< std::make_index_sequence<sizeof...(values)>, Values...>{ using tuple_base< std::make_index_sequence<sizeof...(values)>, Values... >::tuple_base;};</sizeof...(values)></sizeof...(values)></class></std::index_sequence<></i,></i></i...></std::size_t...></class></std::size_t>
base _ from _ member 用来保存 tuple 元素的键值和值,tuple _ base 派生于 base _ from _ member,自动生成 tuple 中每一个类型的 base _ from _ member,tuple 派生于 tuple _ base 用来简化 tuple _ base 的定义。再给 tuple 增加一个根据键值获取元素的辅助手段。
template <std::size_t n,="" class="" t="">constexpr const T& get_impl(const base_from_member<n, t="">& t) noexcept { return t.value;}template <std::size_t n,="" class="" ...t="">constexpr decltype(auto) get(const tuple<t...>& t) noexcept { static_assert(N < tuple<t...>::size_v, "Tuple index out of bounds"); return get_impl<n>(t);}</n></t...></t...></std::size_t></n,></std::size_t>
这样就可以通过 get 就可以获得 tuple 中的元素了。
到此,magic _ get 的核心代码分析完了。由于实际的代码会更复杂,为了让观众能更容易看懂,我选择的是简化版的代码,完整的代码可以参考 GitHub 上的 magicget 或者简化版的代码。
magic _ get 实现了对 pod 类型的反射,可以直接借助索引来访问 pod 结构体的数组,而不需要任何额外的宏、标记或软件,确实很 magic。magic _ get 主要是借助 C++11/14 的可变模版参数、constexpr、index _ sequence、pod 构造函数以及这些模板元方法推动的。那么 magic _ get 可以拿来做些什么呢?根据 magic _ get 无需额外的负担和代码就可以推动编译期反射的特征,很合适做 ORM 访问引擎和通用的序列化/反序列化库,我相信还有更多潜力和应用等待我们去发掘。
Modern C++ 的一些虽然平淡无奇的特点组合在一起就能形成奇特的魔力,让人不禁感叹 Modern C++ 蕴藏了无限的可能性与神奇。
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-129594-3.html
在十二海里范围内布置水雷
最爱的女神和最爱的男神配戏啦