在遍历非静态数据成员时,`template for` 不是constexpr
我在gcc 16.1中尝试新的C++26类型反射特性,并有一段用于将结构体/类转换为JSON的基础代码(见文末)。
我的代码:
#include <meta>
#include <string>
#include <iostream>
namespace __impl {
template<auto... vals>
struct replicator_type {
template<typename F>
constexpr void operator>>(F body) const {
(body.template operator()<vals>(), ...);
}
};
template<auto... vals>
replicator_type<vals...> replicator = {};
}
template<typename R>
consteval auto expand(R range) {
std::vector<std::meta::info> args;
for (auto r : range) {
args.push_back(^^r);
}
return std::meta::substitute(^^__impl::replicator, args);
}
template <typename ValueT> constexpr auto ToJson(const ValueT& Value)
{
if constexpr (not std::is_class_v<ValueT>)
{
return std::to_string(Value);
}
else
{
std::string OutputResult = "{\n";
template for (constexpr std::meta::info Member : std::meta::nonstatic_data_members_of(^^ValueT, std::meta::access_context::current()))
{
OutputResult += '"'
+ std::string(std::meta::identifier_of(Member))
+ "\": "
+ ToJson(Value.[:Member:])
+ ",\n";
}
// or with the [: expand() :] workaround:
// [: expand(std::meta::nonstatic_data_members_of(^^ValueT, std::meta::access_context::current())) :] >> [&]<auto Member>
// {
// OutputResult += '"'
// + std::string(std::meta::identifier_of(Member))
// + "\": "
// + ToJson(Value.[:Member:])
// + ",\n";
// };
return OutputResult + "}";
}
}
struct EpicClass
{
public:
int MyInt = 4;
struct
{
int OtherInt = 5;
char Guy = 0;
} Other;
};
int main()
{
EpicClass g_Epic = EpicClass();
std::cout << ToJson(g_Epic) << "\n";
}
然而,在尝试编译时,我遇到了这个错误:
$ /opt/gcc-16.1/bin/g++ --version
g++ (GCC) 16.1.0
Copyright (C) 2026 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ /opt/gcc-16.1/bin/g++ -std=c++26 main.cpp -o app -freflection
main.cpp: In instantiation of ‘constexpr auto ToJson(const ValueT&) [with ValueT = EpicClass]’:
main.cpp:48:24: required from here
48 | std::cout << ToJson(g_Epic) << "\n";
| ~~~~~~^~~~~~~~
main.cpp:19:9: error: ‘std::meta::nonstatic_data_members_of(^^EpicClass, std::meta::access_context::current())’ is not a constant expression because it refers to a result of ‘operator new’
19 | template for (constexpr std::meta::info Member : std::meta::nonstatic_data_members_of(^^ValueT, std::meta::access_context::current()))
| ^~~~~~~~
In file included from /opt/gcc-16.1/include/c++/16.1.0/string:46,
from /opt/gcc-16.1/include/c++/16.1.0/bits/stdexcept_throw.h:57,
from /opt/gcc-16.1/include/c++/16.1.0/array:44,
from /opt/gcc-16.1/include/c++/16.1.0/meta:42,
from main.cpp:2:
/opt/gcc-16.1/include/c++/16.1.0/bits/allocator.h:203:52: note: allocated here
203 | return static_cast<_Tp*>(::operator new(__n));
| ~~~~~~~~~~~~~~^~~~~
我尝试使用展开变通方法(见 https://isocpp.org/files/papers/P2996R4.html#implementation-status),但又遇到了另一个错误:
main.cpp: In instantiation of ‘constexpr auto ToJson(const ValueT&) [with ValueT = EpicClass]’:
main.cpp:67:24: required from here
67 | std::cout << ToJson(g_Epic) << "\n";
| ~~~~~~^~~~~~~~
main.cpp:38:12: error: uncaught exception of type ‘std::meta::exception’; ‘what()’: ‘can_substitute returned false’
38 | [: expand(std::meta::nonstatic_data_members_of(^^ValueT, std::meta::access_context::current())) :] >> [&]<auto Member>
| ~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
有什么建议吗?
解决方案
问题在于 std::meta::nonstatic_data_members_of 会返回 std::vector<std::meta::info>。
并且由于 std::vector 可能分配内存,因此不能跨越反射的编译时求值区域的边界。
为了解决它,你可以使用 std::define_static_array 将来自 nonstatic_data_members_of 的结果包装成一个“对编译时友好”的数组(通过提升到静态存储)。
也就是说,不使用:
template for (constexpr std::meta::info Member : std::meta::nonstatic_data_members_of(^^ValueT, std::meta::access_context::current()))
而是使用:
//-----------------------------------------------vvvvvvvvvvvvvvvvvvvvvvvv
template for (constexpr std::meta::info Member : std::define_static_array(std::meta::nonstatic_data_members_of(^^ValueT, std::meta::access_context::current())))
输出应该是:
{
"MyInt": 4,
"Other": {
"OtherInt": 5,
"Guy": 0,
},
}
Live on Godbolt
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。