JSON for Modern C++ 中 nlohmann::basic_json::empty() 的深度解析:返回值语义、源码实现与测试验证
【免费下载链接】jsonJSON for Modern C++项目地址: https://gitcode.com/GitHub_Trending/js/json
本篇指南基于官方 API 文档,详解nlohmann::basic_json::empty()接口在 JSON for Modern C++(nlohmann/json)中的作用与语义:它如何判断一个 JSON 值"没有元素"、对九种值类型分别返回什么,并结合 库头文件 中的真实实现与 容量单元测试 验证其正确性。读完后,你将掌握该接口的返回值规则、源码级委托机制,以及它与size()、迭代器定义之间的等价关系,能够在实际工程中安全地用它做容器状态检查。
函数签名与语义
empty()的完整签名为:
bool empty() const noexcept;官方定义是:检查一个 JSON 值是否没有元素,即其size()是否为0。这是一个const成员函数,带有 no-throw 保证,可以在任何需要只读检查的场景中放心调用,包括范围检查、条件分支和断言。
从源码位置看,该函数位于 include/nlohmann/json.hpp 中capacity(容量)功能组的开头,紧邻size()与max_size(),与 C++ 标准容器的 capacity 接口组布局一致。
各值类型的返回值
官方文档给出了完整的返回值定义表:
| 值类型 | 返回值 |
|---|---|
| null | true |
| boolean | false |
| string | false |
| number | false |
| binary | false |
| object | object_t::empty()的结果 |
| array | array_t::empty()的结果 |
几个值得注意的点:
null是唯一"天然为空"的标量类型。null不持有任何数据,因此被视为空。- 所有其他标量类型(布尔、字符串、数字、二进制)一律返回
false。即使字符串内容长度为 0(即json j = "";),empty()也返回false——因为 JSON 容器本身持有一个 string 值,而不是"没有元素"。官方文档专门用 Notes 一节强调了这一点:empty()判断的是"JSON 容器本身是否为空",而不是"容器里存的字符串是否为空"。如需判断字符串内容长度,应先取回string_t再调用其自身的empty()。 - object 与 array 才是真正"有元素个数"的复合类型,其返回值委托给底层容器的
empty()。对于默认配置(array_t = std::vector<basic_json>、object_t = std::map<std::string, basic_json>),这意味着委托给标准库容器的 O(1)empty()。
源码实现解析
官方文档给出的"参考实现"非常简单:
bool empty() const noexcept { return size() == 0; }但 include/nlohmann/json.hpp 中的实际实现并没有走size(),而是按m_data.m_type直接做类型分派,避免了一次间接调用:
bool empty() const noexcept { switch (m_data.m_type) { case value_t::null: { // null values are empty return true; } case value_t::array: { // delegate call to array_t::empty() return m_data.m_value.array->empty(); } case value_t::object: { // delegate call to object_t::empty() return m_data.m_value.object->empty(); } 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: { // all other types are nonempty return false; } } }可以从中读出三点实现细节:
basic_json是"类型标签 + 联合数据"结构。内部成员m_data.m_type记录当前值属于value_t枚举的哪一种,m_data.m_value中则存放对应的实际容器(array指针、object指针等)。empty()的第一层判断完全由类型标签驱动。- 委托而非计算。对 array/object,函数直接把判断交给
array_t/object_t的empty(),因此行为与所用容器类型严格一致。若用户通过模板参数改用 ordered_map 作为 object 容器(即ordered_json),从源码结构看ordered_map继承自std::vector<std::pair<const Key, T>>(见 include/nlohmann/ordered_map.hpp),其empty()同样是 O(1),空对象判断结果与std::map版本完全一致。 discarded类型归入"非空"分支。解析过程中被丢弃(discarded)的值与字符串、布尔、数字、二进制一样返回false,这与文档表中"其余类型返回false"的语义在实现上闭合。
与之对照,同文件中的size()实现采用同一套分派结构:null返回0,array/object 委托size(),其余类型返回1。因此文档中"empty()即size() == 0"的定义在两种实现路径下都成立——实际实现只是省去了size()的中间跳转,直接对类型标签分派。
完整示例与运行结果
官方示例(源码见 empty.cpp)覆盖 null、布尔、整数、浮点、对象、空对象、数组、空数组、字符串共九种情况:
#include <iostream> #include <nlohmann/json.hpp> using json = nlohmann::json; int main() { // create JSON values json j_null; json j_boolean = true; json j_number_integer = 17; json j_number_float = 23.42; json j_object = {{"one", 1}, {"two", 2}}; json j_object_empty(json::value_t::object); json j_array = {1, 2, 4, 8, 16}; json j_array_empty(json::value_t::array); json j_string = "Hello, world"; // call empty() std::cout << std::boolalpha; std::cout << j_null.empty() << '\n'; std::cout << j_boolean.empty() << '\n'; std::cout << j_number_integer.empty() << '\n'; std::cout << j_number_float.empty() << '\n'; std::cout << j_object.empty() << '\n'; std::cout << j_object_empty.empty() << '\n'; std::cout << j_array.empty() << '\n'; std::cout << j_array_empty.empty() << '\n'; std::cout << j_string.empty() << '\n'; }运行输出(与 empty.output 一致):
true false false false false true false true false逐项对照即可验证上表的语义:j_null、j_object_empty、j_array_empty为true;布尔、整数、浮点、双元素对象、五元素数组、非空字符串均为false。注意json j_null;的默认构造即 null 值,这解释了第一行为true。
单元测试对语义的进一步验证
tests/src/unit-capacity.cpp 中的TEST_CASE("capacity")对empty()做了系统性验证,覆盖 boolean、string、array(空/非空)、object(空/非空)、整数、无符号整数、浮点、null 八类场景。除断言各类型的返回真值外(例如空数组j.empty() == true、非空对象j.empty() == false),每个场景都额外验证了 C++ 标准库对"空"的定义式等价关系:
CHECK(j.empty() == (j.begin() == j.end()));即empty()的结果与begin() == end()一致。这说明empty()的行为完全符合标准容器语义,可以安全地用于"是否需要跳过处理"这类判断,且对const与非常量对象的行为一致(测试中对j和j_const均做了断言)。
异常安全与复杂度
- 异常安全:No-throw guarantee,该函数从不抛出异常(函数签名中的
noexcept在 源码 中直接可见)。 - 时间复杂度:常数量级——前提是
array_t与object_t满足 C++ 标准 Container 概念(即其empty()为 O(1))。默认的std::vector与std::map均满足该条件。
版本历史与使用建议
empty()自version 1.0.0起提供。- version 3.8.0起扩展为对 binary 类型返回
false(binary 支持本身即在该版本引入,之前的版本不存在这一分支)。
实践建议:
- 判断"对象/数组里有没有内容"时优先用
empty(),而不是size() == 0或begin() == end(),语义更清晰且与库的实现路径一致; - 判断"字符串值是否为空串"时不要用
j.empty(),应先用j.is_string()确认类型,再对取回的string_t判断长度; - 对解析后的
null值做判空时,empty()返回true,若需区分 null 与空容器,可配合is_null()、is_array()、is_object()使用。
相关接口
size():empty()定义的直接依据;array_t/object_t:决定委托行为与 O(1) 复杂度的底层容器类型;- ordered_map:用于
ordered_json的保序对象容器,同样支持 O(1) 的empty()委托。
【免费下载链接】jsonJSON for Modern C++项目地址: https://gitcode.com/GitHub_Trending/js/json
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考