Boost.Pfr是否支持以成员指针形式遍历类型的字段
结论
Boost.Pfr 原生提供的 boost::pfr::for_each_field 接口仅会将遍历到的字段以引用形式传入回调,没有内置直接传递 T A::* 类型类成员指针的功能。但你完全可以基于Pfr已有的编译期反射能力,用少量代码实现符合你预期的用法,没有额外运行时开销,和原生接口的适用范围完全一致。
Pfr遍历字段的核心逻辑是基于编译期索引展开:你可以通过boost::pfr::tuple_size_v<T>拿到类型的字段总数,通过boost::pfr::tuple_element_t<I, T>拿到第I个字段的类型,再通过标准语法构造出对应位置的成员指针即可。
可直接复用的实现代码
#include <boost/pfr.hpp> #include <string> #include <utility> #include <type_traits> struct A { int number; std::string text; }; // 编译期索引展开辅助实现 template <typename T, typename F, std::size_t... Is> void for_each_field_mem_ptr_impl(T&& obj, F&& f, std::index_sequence<Is...>) { using unqualified_T = std::remove_cvref_t<T>; // 按索引构造对应类型的成员指针,传入回调 (f( static_cast<boost::pfr::tuple_element_t<Is, unqualified_T> unqualified_T::*>( &boost::pfr::get<Is>(std::declval<unqualified_T&>()) ), boost::pfr::get<Is>(std::forward<T>(obj)) // 如果不需要同时拿到字段引用可以删掉这行 ), ...); } // 对外接口,用法和boost::pfr::for_each_field对齐 template <typename T, typename F> void for_each_field_mem_ptr(T&& obj, F&& f) { using unqualified_T = std::remove_cvref_t<T>; for_each_field_mem_ptr_impl( std::forward<T>(obj), std::forward<F>(f), std::make_index_sequence<boost::pfr::tuple_size_v<unqualified_T>>{} ); }
使用示例(完全匹配你期望的写法)
int main() { for_each_field_mem_ptr(A{1, "foo"}, [](auto A::* field, auto& field_value) { // 这里的field就是你需要的T A::*类型成员指针 // field_value是对应字段的引用,不需要可以在实现里删掉 A test_obj{42, "bar"}; auto& val = test_obj.*field; // 直接通过成员指针访问 } ); // 正确性验证:生成的成员指针和手动取的成员地址完全一致 for_each_field_mem_ptr(A{1, "foo"}, [](auto A::* field, auto&) { if constexpr (std::is_same_v<decltype(field), int A::*>) { static_assert(&A::number == field); } if constexpr (std::is_same_v<decltype(field), std::string A::*>) { static_assert(&A::text == field); } } ); }
注意事项
- 该实现仅对Boost.Pfr支持的聚合类型生效,即所有非静态成员均为公有、无用户声明构造函数的聚合类型,和原生
for_each_field的适配范围完全相同。 - 整个逻辑在编译期完成展开,运行时无任何额外开销,生成的成员指针和你手动写
&A::number、&A::text得到的指针完全一致,可以直接通过.*/->*运算符访问。 - 如果只需要成员指针不需要字段引用,删掉辅助实现里传
boost::pfr::get<Is>(...)的参数即可。
内容的提问来源于stack exchange,提问作者Nick Strupat
相关产品推荐
相关产品推荐

