C++ JsonPointer类:initializer_list constexpr初始化与模板尺寸推导
JsonPointer类的constexpr初始化与扩展构造函数实现
需求与现有代码
需要实现一个封装std::array的JsonPointer模板类,满足:
- 支持通过
std::initializer_list进行constexpr初始化 - 提供构造函数/方法,接收现有
JsonPointer实例和一个后缀字符串,生成尺寸为原实例尺寸+1的新实例
现有代码如下:
#include <stddef.h> #include <array> #include <initializer_list> template<size_t L> class JsonPointer { public: constexpr JsonPointer(std::array<const char*, L> segments) : m_path_segments(std::move(segments)) { } constexpr JsonPointer(std::initializer_list<const char*> list) : m_path_segments(std::array<const char*, list.size()>) { } template<size_t S> JsonPointer(JsonPointer<S> existing, const char* suffix) { } private: std::array<const char*, L> m_path_segments; }; int main(){ constexpr JsonPointer base_ptr {"a", "n"}; auto ptr = JsonPointer(base_ptr, "test"); }
问题解答
1. 利用std::initializer_list的大小实现constexpr初始化
原代码的问题在于:无法从initializer_list推导模板参数L,且未完成列表元素到内部数组的拷贝。解决步骤:
- 添加模板推导指引,让编译器从初始化列表的元素数量自动推导
L - 在constexpr构造函数中,通过循环(C17+支持constexpr循环)或
std::copy_n(C20+支持constexpr版本)完成元素拷贝,同时用static_assert确保列表大小与L匹配
2. 让模板推导识别新实例尺寸L = S + 1
原构造函数无法让编译器推导L的值,因为L是类模板参数,无法直接从构造函数参数推导。解决方式有两种:
- 提供
append成员函数,直接返回JsonPointer<S+1>类型,编译器可明确推导返回值 - 为构造函数添加推导指引,明确声明
L = S + 1
修改后的完整代码
#include <stddef.h> #include <array> #include <initializer_list> template<size_t L> class JsonPointer { public: // 从std::array构造 constexpr JsonPointer(std::array<const char*, L> segments) : m_path_segments(std::move(segments)) { } // 从std::initializer_list构造:constexpr拷贝元素(兼容C++17) constexpr JsonPointer(std::initializer_list<const char*> list) { static_assert(list.size() == L, "Initializer list size mismatch with JsonPointer length"); size_t idx = 0; for (const char* str : list) { m_path_segments[idx++] = str; } } // 方式一:通过成员函数生成尺寸+1的新实例 constexpr JsonPointer<L + 1> append(const char* suffix) const { std::array<const char*, L + 1> new_segments{}; // 拷贝原数组元素 size_t idx = 0; for (const char* str : m_path_segments) { new_segments[idx++] = str; } // 添加后缀 new_segments[L] = suffix; return JsonPointer<L + 1>(new_segments); } // 方式二:通过构造函数生成,配合下方推导指引 template<size_t S> constexpr JsonPointer(JsonPointer<S> existing, const char* suffix) { static_assert(L == S + 1, "New JsonPointer length must be S+1"); // 拷贝原实例元素 size_t idx = 0; for (const char* str : existing.m_path_segments) { m_path_segments[idx++] = str; } // 添加后缀 m_path_segments[S] = suffix; } private: std::array<const char*, L> m_path_segments; // 让构造函数能访问其他实例的私有成员 template<size_t> friend class JsonPointer; }; // 推导指引1:从初始化列表推导L template<typename... Args> JsonPointer(Args&&... args) -> JsonPointer<sizeof...(Args)>; // 推导指引2:从现有实例+后缀推导L=S+1 template<size_t S> JsonPointer(JsonPointer<S>, const char*) -> JsonPointer<S + 1>; int main(){ // 从initializer_list构造,自动推导L=2 constexpr JsonPointer base_ptr {"a", "n"}; // 使用append成员函数生成L=3的实例 auto ptr1 = base_ptr.append("test"); // 使用构造函数生成L=3的实例(依赖推导指引) auto ptr2 = JsonPointer(base_ptr, "test"); }
关键改动说明
- constexpr初始化:通过constexpr循环完成元素拷贝,推导指引让编译器能从初始化列表的元素数量自动匹配模板参数
L - 尺寸推导:
append成员函数直接返回JsonPointer<L+1>,或通过构造函数推导指引明确L=S+1,两种方式都能让编译器正确识别新实例的尺寸
内容的提问来源于stack exchange,提问作者Curunir
相关产品推荐
相关产品推荐

