首页
/ JSON for Modern C++ 中 basic_json::cbegin() 详解:常时空指针安全的"首元素"迭代器

JSON for Modern C++ 中 basic_json::cbegin() 详解:常时空指针安全的"首元素"迭代器

2026-09-05 17:50:47作者:毕习沙Eudora

在 C++ 中遍历 nlohmann::json 值(对象或数组)时,cbegin() 是获取恒值(const)迭代器的标准入口,它以常时间复杂度返回指向第一个元素的迭代器,且保证绝不抛出异常。本文基于本仓库的 API 文档 docs/mkdocs/docs/api/basic_json/cbegin.md,结合头文件实现与测试用例,完整讲解该函数的签名语义、行为约定、可运行示例,以及它在底层如何按值类型分发迭代器状态。

begin/cbegin 返回指向首元素的迭代器示意图

1. 函数签名与基本语义

cbegin() 声明于 nlohmann::basic_json 类中,签名如下:

const_iterator cbegin() const noexcept;

API 文档 的约定,该函数的各项属性为:

属性 说明
返回值 指向第一个元素的 const_iteratoriterator to the first element
异常安全 无抛出保证(No-throw guarantee):该成员函数从不抛出异常
时间复杂度 常数时间(Constant)
版本历史 自 1.0.0 版本起可用

它返回的是恒值迭代器const_iterator),即通过该迭代器解引用得到的是 const 引用,不能借由迭代器反向修改 JSON 值本身。对于只需"读"的场景(序列化、校验、拷贝子集),cbegin() 是比 begin() 更语义明确的入口。

2. 可运行示例

官方文档给出了最小示例 cbegin.cpp,其完整内容如下:

#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

int main()
{
    // create an array value
    const json array = {1, 2, 3, 4, 5};

    // get an iterator to the first element
    json::const_iterator it = array.cbegin();

    // serialize the element that the iterator points to
    std::cout << *it << '\n';
}

要点说明:

  • 示例中 array 被声明为 const json,因此只能拿到 const_iteratorcbegin() 恰好匹配这一用法;
  • 解引用 *it 得到第一个元素 1
  • 将结果以 JSON 序列化输出,运行结果为 1(对应 cbegin.output)。

该示例可直接复制运行,只需包含头文件 include/nlohmann/json.hpp(或使用单头文件版本 single_include/nlohmann/json.hpp)即可编译。

3. 源码剖析:cbegin() 如何实现"首元素"

json.hpp 中,cbegin() 的实现只有三步:

/// @brief returns a const iterator to the first element
/// @sa https://json.nlohmann.me/api/basic_json/cbegin/
const_iterator cbegin() const noexcept
{
    const_iterator result(this);
    result.set_begin();
    return result;
}

即:构造一个绑定到当前对象的 const_iterator,调用 set_begin() 将内部状态定位到"第一个元素",然后返回。值得注意的是 json.hpp 中的常量版本 begin() 也直接委托给 cbegin()

const_iterator begin() const noexcept
{
    return cbegin();
}

这说明从源码结构看,cbegin() 是所有"只读首元素迭代器"的统一底层实现。

真正的分发生成逻辑在 const_iterator 的底层实现 iter_implset_begin() 中,见 iter_impl.hpp

void set_begin() noexcept
{
    JSON_ASSERT(m_object != nullptr);

    switch (m_object->m_data.m_type)
    {
        case value_t::object:
        {
            m_it.object_iterator = m_object->m_data.m_value.object->begin();
            break;
        }

        case value_t::array:
        {
            m_it.array_iterator = m_object->m_data.m_value.array->begin();
            break;
        }

        case value_t::null:
        {
            // set to end so begin()==end() is true: null is empty
            m_it.primitive_iterator.set_end();
            break;
        }

        case value_t::string:
        case value_t::boolean:
        case value_t::number_integer:
        case value_t::number_unsigned:
        case value_t::number_float:
        case value_t::binary:
        case value_t::discarded:
        default:
        {
            m_it.primitive_iterator.set_begin();
            break;
        }
    }
}

从这段分支逻辑可以读出几个关键行为约定:

  1. object 类型:内部 object_iterator 被设置为底层对象容器的 begin(),指向第一个键值对;
  2. array 类型:内部 array_iterator 被设置为底层数组容器的 begin(),指向第一个元素;
  3. null 类型primitive_iterator 被置为 end(),源码注释明确写道 set to end so begin()==end() is true: null is empty——即对 null 值,cbegin() == cend() 恒成立,遍历循环(如 range-for)零次迭代;
  4. 标量类型(string、boolean、各种 number、binary、discarded):走 primitive_iterator.set_begin()primitive_iteratorprimitive_iterator.hpp 中定义的极简迭代器,其语义是"一个值本身构成一个长度为 1 的序列",因此 cbegin() 指向该值本身,cend() 指向其"后一位"。

这些约定保证了 cbegin() 对任意 JSON 值类型都有定义良好的行为,无需调用者先判断类型——这正是文档中"无抛出、常数时间"承诺的实现基础:整个过程只是设置一个类型标签加一个底层容器迭代器,不做任何分配或构造,自然也不会抛异常。

4. 与其他迭代器入口的关系

basic_json 提供了一组平行的迭代器入口(实现见 json.hpp),理解它们与 cbegin() 的分工有助于避免误用:

方法 返回类型 定位 典型用途
begin()(非常量版本) iterator 首元素 需要经迭代器修改内容时
begin()(常量版本) const_iterator 首元素 内部直接转调 cbegin()
cbegin() const_iterator 首元素 只读遍历,语义最明确
cend() const_iterator 末元素后一位 cbegin() 配对作遍历终点
crbegin() / crend() const_reverse_iterator 反向首/末 反向只读遍历,分别构造自 cend() / cbegin()

可以推断,crend() 正是用 cbegin() 构造反向序列的终点(return const_reverse_iterator(cbegin());),即 cbegin() 同时是整个常量反向迭代体系的基础锚点。

5. 测试用例中的验证方式

仓库的单元测试充分验证了 cbegin() 的行为。在 unit-class_const_iterator.cpp 中,大量 CHECK((it == j.cbegin())) 断言覆盖了对象、数组、null 与各类标量值下"首迭代器相等性"与"解引用取首元素"的场景;而在算法类测试 unit-algorithms.cpp 中,cbegin() 以标准迭代器区间的身份被传入 std::for_each

std::for_each(j_array.cbegin(), j_array.cend(), &sum { ... });

这表明 const_iterator 满足 STL 对迭代器的要求(可比较、可自增、可解引用),可以直接与 <algorithm> 等标准库头文件中的模板配合使用,而不需要任何适配层。

6. 使用建议与小结

  • 只读场景优先 cbegin():当对象为 const json,或逻辑上不应通过迭代器修改值时,显式使用 cbegin() 可让"只读"意图在代码中可见;
  • cend() 配对:完整遍历应写成 for (auto it = j.cbegin(); it != j.cend(); ++it),或直接用 range-for(其内部即调用 begin()/end(),常量版本最终落到 cbegin()/cend());
  • 不要假设首元素存在:对 null 值 cbegin() == cend(),对空对象/空数组同样相等;因此遍历前先判断 is_object()/is_array() 或依赖循环自然退出是安全的;
  • 常数时间与无抛出是硬保证:从 iter_impl.hpp 的实现看,cbegin() 不含分配、I/O 或依赖值内容的分支,可放心用于热路径。

综上,cbegin() 虽小,却是 nlohmann::json 迭代器体系的只读锚点:它按值类型(object/array/null/标量)分发生成恒值迭代器,保证常数时间、无异常,并与 cend()crend()、STL 算法无缝协作。结合 API 文档示例代码迭代器底层实现,即可完整掌握其语义与边界行为。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.79 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
988
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384