通过C++26的反射,将C++ Lambda的捕获映射到POD结构体(P2996)

编程语言 2026-07-11

我正在研究使用C++26的反射(P2996)将带状态的lambda的捕获列表反射到一个可命名的POD结构体中。我的目标是“滥用”编译器通常将lambda实现为无名类这一点。相关代码已放在下方,并且也可在Compiler Explorer上查看:这里

我在两大实现的反射实现之间看到的结果差异很大:

  • Clang(p2996分支):正确镜像了捕获。输出是 Success!。看起来Clang将捕获视为标准可反射的数据成员。
  • GCC(trunk):输出是 Reflection failed: size mismatch (1 vs 16)。似乎 nonstatic_data_members_of 为闭包类型返回了一个空的范围,导致得到一个空的1 字节结构体。

GCC是否以一种根本性的方式实现lambda捕获,使得反射引擎根本无法将其视为数据成员?P2996的初衷是最终支持对lambda捕获列表的反射,还是目前的这一行为属于实现定义/不可接受的?我是不是完全误解了什么?

#include <bit>
#include <iostream>
#include <meta>
#include <vector>

template<typename M, typename Lambda>
void try_mirror(Lambda&& l) {
    if constexpr (sizeof(M) == sizeof(Lambda)) {
        // This is taken in Clang (expected)
        auto m = std::bit_cast<M>(l);
        std::cout << "Success!\n";
    } else {
        // This is taken on GCC (why?)
        std::cout << "Reflection failed: size mismatch (" 
                  << sizeof(M) << " vs " << sizeof(Lambda) << ")\n";
    }
}

int main() {
    int id = 101;
    double val = 99.9;
    auto lambda = [id, val](int x) { return id + x + (int)val; };

    using L = decltype(lambda);
    struct Mirror; // Want to send captured members here

    consteval {
        using namespace std::meta;
        constexpr auto ctx = access_context::unchecked();
        std::vector<info> specs;

        // Was expecting to see all captured members here
        template for (constexpr auto c : std::define_static_array(
                          nonstatic_data_members_of(^^L, ctx))) 
        {
            specs.push_back(data_member_spec(type_of(c), {}));
        }

        define_aggregate(^^Mirror, specs);
    }

    try_mirror<Mirror>(lambda);
    return 0;
}

为什么我要这样做

我们正在致力于在HPX中实现对lambda序列化的自动化。我们最近为使用反射序列化的一般类添加了支持。如果一个lambda的捕获列表能够镜像成一个POD结构体,我们现有的序列化基础设施就能够处理它。

解决方案

此行为当前是实现定义的。

根据 [meta.reflection.member.queries]nonstatic_data_members_of(r, ctx)(其中 r 是对类或命名空间Q 的反射)返回对那些属于 Q-members-of-representable 的成员M 的反射。为了让一个成员成为 Q-members-of-representable,该成员必须具备一个 Q-members-of-eligible 声明。

Q-members-of-eligible 的要求包括(其中之一):

如果Q 是闭包类型,则M 必须是函数调用运算符或函数调用运算符模板。

对一个闭包类型Q 的其他成员是否为Q-members-of-eligible,属于实现定义。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章