假設我們現在處理struct中每個field的時候,該怎麼做才好
Pre C++20
Boost PFR提供了一個方案
1 2 3 4
| struct my_struct { int i; short s; }; int sum = 0; boost::pfr::for_each_field(my_struct{20, 22}, [&sum](const auto& field) { sum += field; }); assert(sum == 42);
|
不過還是有它的問題
- 需要Boost PFR,增加Dependency overhead
- 雖然看起來像迴圈,不過不能進行break或continue的控制
Tuple & std::make_index_sequence
如果把問題簡化一下,只支援tuple的話,可以寫成
1 2 3 4 5 6 7 8 9 10 11
| template <typename... args_t, typename Func> auto traverse_tuple(std::tuple<args_t...>& t, Func func) { [&]<size_t... Is>(std::index_sequence<Is...>){ ([&](auto&& v){ func(v); }(std::get<Is>(t)), ...); }(std::make_index_sequence<sizeof...(args_t)>{}); } std::tuple<int, short> v(20, 22); int sum = 0; traverse_tuple(v, [&](auto v) { sum += v; });
|
雖然減少了對Boost PFR的依賴
不過問題的複雜度就變成了
- 將struct變成tuple這件事,處理一般性的struct是很難的一件事,直到C++26 Reflection來到
- 一樣不能做break 或 continue
C++26 Reflection
在還沒有Expansion Statements之前,Reflection論文中就提供了一種變通的方案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| namespace __impl { template<auto... vals> struct replicator_type { template<typename F> constexpr void operator>>(F body) const { (body.template operator()<vals>(), ...); } }; template<auto... vals> replicator_type<vals...> replicator = {}; }
template<typename R> consteval auto expand(R range) { std::vector<std::meta::info> args; for (auto r : range) { args.push_back(std::meta::reflect_constant(r)); } return substitute(^^__impl::replicator, args); } my_struct v { 20, 22 }; int sum = 0; [: expand(nonstatic_data_members_of(^^my_struct, std::meta::access_context::current())) :] >> [&]<auto e>() { sum += v.[: e :]; };
|
雖然可以用,不過
- 難以理解
- 還是不能break 或 continue
Expansion Statements
終於到本文重點了,就當作是一般的for loop就好了
1 2 3 4 5 6 7 8
| int main() { my_struct v {20, 22}; int sum = 0; template for (auto elem : v) { if (elem == 20) continue; sum += elem; } }
|
Reference