You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 01:52:08