Herb Sutter 刚刚宣布结论:C++26,即 C++ 的下一个版本,将包含编译时反射。
编程语言中的反射意味着您可以访问代码自身的结构。例如,您可以获取一个类,并枚举其方法。例如,您可以接收一个类,检查它是否包含返回字符串的方法,然后调用此方法并获取字符串。大多数编程语言都支持某种形式的反射。例如,老牌的 Java 就完全支持反射。
然而,C++ 正在获得编译时反射。这是一个重要的发展。
几个月前,我宣布,由于与 Francisco Geiman Thiesen 的合作,只要主流编译器支持,性能导向的 JSON 库 simdjson 就会支持编译时反射。
这使得您可以采用自己的数据结构并将其转换为 JSON 字符串,而无需付出任何努力,而且速度很快:
孩子 k{12, "约翰", {"汽车", "球"}}; simdjson::to_json(k); // 将生成 {"age": 12, "name": "John", "toys": ["car", "ball"]}
您还可以返回,给定一个 JSON 文档,您可以返回自定义类型的实例:
孩子 k = doc.get<kid>();
代码可以在主库中进行高度优化和全面测试。无需样板代码有诸多好处。
为了进一步说明这个想法,让我考虑一下对象到SQL映射的情况。假设你有一个自定义类型:
结构用户{ int id ; std ::字符串名称; 双平衡; 私人的: int secret ; // 在 SQL 生成中忽略 } ;
你想把这个用户的实例插入到数据库中。你需要把它转换成如下字符串:
插入 tbl (id, name, balance) 值 (0, '', 0.000000);
这有多简单?通过编译时反射,我们可以实现高效且简单的单函数调用:
生成_sql_insert ( u , “表” ) ;
当然,繁重的工作仍需完成。但只需做一次。
它看起来像什么?首先,我们要生成列字符串(例如,id、name、balance)。
我还没有接触过真正的 C++26 编译器。C++26 正式发布后,我们会引入一些类似“template for”的功能,它类似于“for”循环,但用于模板元编程。同时,我使用了一种比较晦涩的“expand”语法。
代码仍然是合理的:
模板<类型名称T > consteval std :: string generate_sql_columns ( ) { std ::字符串列; bool first = true ; constexpr auto ctx = std :: meta :: access_context :: current ( ) ; [ :扩展( std :: meta :: nonstatic_data_members_of ( ^ ^ T , ctx ) ) : ] > > [ & ] <自动成员> { 使用member_type = typename [ : type_of ( member ) : ] ; 如果( !第一个) { 列+ = “ , ” ; } 第一个=假; // 获取会员名称 自动名称= std :: meta :: identifier_of (成员) ; 列+ =名称; } ; 返回列; }
此函数是“consteval”函数,这意味着您应该期望它在编译时进行求值。因此它非常高效:字符串在编译代码时计算。因此,以下函数可能只返回一个预先计算的字符串:
std ::字符串g ( ) { 返回generate_sql_columns <用户> ( ) ; }
接下来我们需要计算这些值的字符串(例如,(0, ”, 0.000000))。这有点棘手。你需要转义字符串,并处理不同的值类型。这是一个不错的草图:
模板<类型名称T > constexpr std :: string generate_sql_valuess ( const T & obj ) { std ::字符串值; bool first = true ; constexpr auto ctx = std :: meta :: access_context :: current ( ) ; [ :扩展( std :: meta :: nonstatic_data_members_of ( ^ ^ T , ctx ) ) : ] > > [ & ] <自动成员> { 使用member_type = typename [ : type_of ( member ) : ] ; 如果( !第一个) { 值+ = “ , ” ; } 第一个=假; // 获取成员值 自动值= obj . [ : member : ] ; // 根据类型格式化值 如果constexpr ( std :: same_as <成员类型, std :: string > ) { // 转义字符串中的单引号 std ::字符串转义=值; size_t位置= 0 ; while ( ( pos = escaped.find ( ' \ '' , pos ) ) ! = std :: string :: npos ) { 转义. replace ( pos , 1 , " ' ' ) ; 位置+ = 2 ; } 值+ = “ ' ” +转义+ “ ' ” ; }否则,如果constexpr ( std :: is_arithmetic_v < member_type > ) { 值+ = std :: to_string (值) ; } } ; 返回值; }
现在你可以把它们放在一起:
模板<类型名称T > constexpr std :: string generate_sql_insert ( const T & obj , const std :: string & table_name ) { constexpr std :: string列= generate_sql_columns < T > ( ) ; std ::字符串值= generate_sql_valuess ( obj ) ; 返回“ INSERT INTO ” +表名 + “ ( ” +列+ “ )值( ” +值+ “ ); ” ; }
它只是众多应用之一。其核心思想是,你可以编写高度优化且非常安全的代码,这些代码可以在许多情况下重用。代码看起来有点吓人,就像 C++ 那样,但它相当合理。
在未来的几年里,许多项目将通过编译时反射得到简化和优化。
代码:我已经在我的博客代码库中发布了完整的实现。我相信它还能有显著的改进。
原文: https://lemire.me/blog/2025/06/22/c26-will-include-compile-time-reflection-why-should-you-care/