如何用C++20的std::source_location::function_name提取结构体数据成员名
C++20 兼容多编译器的结构体成员名提取方案
问题需求
给定满足以下条件的struct_t类型:
- 包含任意数量非静态数据成员
- 支持结构化绑定(
auto& [a,b,c,...] = my_struct)
要求仅使用C++20标准库与std::source_location::function_name,提取所有非静态数据成员名,需满足: - 无需依赖结构体的拷贝/移动构造函数
- 兼容g++、clang、MSVC
- 可轻松扩展至任意字段数
原示例代码的问题
原示例通过静态实例获取成员地址,但存在以下缺陷:
- 依赖结构体的拷贝/移动构造函数,无法处理无此类构造的类型
- 静态实例的写法不兼容MSVC
- 仅支持固定3个字段,扩展性差
解决方案
核心思路:利用std::declval创建未求值的类型引用,结合模板参数包与折叠表达式处理任意字段,再从std::source_location的函数名中解析成员名。
完整实现代码
#include <iostream> #include <source_location> #include <string_view> #include <tuple> #include <utility> #include <array> #include <algorithm> // 辅助函数:从source_location的function_name中提取成员名 constexpr std::string_view extract_member_name(std::string_view func_name) { // 处理g++/clang格式:例如 "std::string_view get() [p = &std::declval<struct_t>().field1]" auto ptr_pos = func_name.find("&"); if (ptr_pos != std::string_view::npos) { func_name = func_name.substr(ptr_pos + 1); } // 处理MSVC格式:例如 "std::string_view __cdecl get<&struct_t::field1>(void)" auto colon_pos = func_name.find("::"); if (colon_pos != std::string_view::npos) { func_name = func_name.substr(colon_pos + 2); } // 去掉末尾的无关字符(如空格、]、)等) auto end_pos = func_name.find_first_of(" ])"); if (end_pos != std::string_view::npos) { func_name = func_name.substr(0, end_pos); } return func_name; } // 模板函数:获取成员指针对应的名称 template<auto MemberPtr> constexpr std::string_view get_member_name() { return extract_member_name(std::source_location::current().function_name()); } // 核心模板:处理结构化绑定的字段 template<typename T, std::size_t... Idx> constexpr auto get_fields_impl(std::index_sequence<Idx...>) { // 使用std::declval获取未求值的引用,无需构造对象 auto&& [dummy...] = std::declval<T>(); // 折叠表达式生成成员指针数组 return std::array{get_member_name<&std::get<Idx>(std::declval<T>())>()...}; } // 对外接口:获取T的所有成员名 template<typename T> constexpr auto get_fields() { // 利用结构化绑定的tuple_size获取字段数量 constexpr auto field_count = std::tuple_size_v<std::remove_reference_t<T>>; return get_fields_impl<T>(std::make_index_sequence<field_count>()); } // 测试结构体 struct struct_t { int field1; double field2; std::vector<int> field3; // 故意删除拷贝构造,验证方案无需依赖 struct_t(const struct_t&) = delete; }; int main() { constexpr auto fields = get_fields<struct_t>(); for (auto name : fields) { std::cout << name << "\n"; } // 输出: // field1 // field2 // field3 return 0; }
关键说明
- 无实例化依赖:使用
std::declval<T>()生成未求值的类型引用,不需要实际构造结构体对象,因此不依赖拷贝/移动构造函数。 - 多编译器兼容:
extract_member_name函数处理g++/clang和MSVC不同的function_name输出格式,统一提取成员名。 - 任意字段扩展:通过
std::tuple_size获取结构化绑定的字段数量,结合std::index_sequence和折叠表达式,自动适配任意数量的成员。 - 编译期计算:所有逻辑均可在编译期完成,运行时无额外开销。
内容的提问来源于stack exchange,提问作者PavelDev
相关产品推荐
相关产品推荐

