C++26中使用反射实现自动化相等性检查
在C++中,当比较两个简单类型(如整数或字符串)的对象是否相等时,操作很简单。但是对于复杂的嵌套结构呢?你可能需要为每个类手动实现比较操作(operator==),这既容易出错又繁琐。
考虑一个person类:
|
|
要比较两个person对象,我们需要检查它们的姓名、年龄、爱好和薪水是否匹配。爱好是hobby对象的向量,每个hobby对象都有一个名称。薪水是可选的。手动为此编写operator==将涉及检查每个字段,如果hobby发生变化,你必须更新它。
如果没有反射,如果你不想依赖C++的默认比较,你必须编写类似这样的丑陋代码:
|
|
但这假设hobby有operator==,而std::vector
相反,我写了一个小例子,完全自动化了这个过程。你只需添加一个操作符重载:
|
|
技巧在于C++26允许你在编译时查询类型的成员并遍历它们。函数std::meta::nonstatic_data_members_of为我们提供了成员列表。
我的完整实现不到一百行代码,归结为以下几行代码:
|
|
它依赖于反射操作符^^T,该操作符生成一个表示类型本身的std::meta::info值。在函数体内,std::meta::nonstatic_data_members_of(^^T, …)查询T的所有非静态数据成员,按声明顺序返回反射值的向量。未检查的访问上下文故意绕过可见性规则,允许比较私有和受保护成员。然后,这个反射向量被包装在std::define_static_array中,将其具体化为静态数组,使其可在编译时循环中迭代。模板for循环在编译时迭代每个成员反射mem。对于每个成员,拼接表达式a.[:mem:]直接访问相应的成员。[: :]大致是反射操作符(^^T)的逆操作:可以将其视为通过^^进入元宇宙,然后通过[: :]返回到标准C++宇宙。
这种方法确保了零运行时开销,因为所有反射、拼接和循环都在编译期间解析。作为程序员,它让你可以完全控制如何在应用程序中实现比较。我发布了一个完整的演示。
Daniel Lemire, “Automated Equality Checks in C++ with Reflection (C++26),” in Daniel Lemire’s blog, November 9, 2025, https://lemire.me/blog/2025/11/09/automated-equality-checks-in-c-with-reflection-c26/.