在遍历非静态数据成员时,`template for` 不是constexpr

编程语言 2026-07-09

我在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导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章