资讯详情

资讯详情

JSON for Modern C++ 中 basic_json::default_object_comparator_t 详解:C++14 透明比较器如何避免键查询的字符串构造

JSON for Modern C 中 basic_json::default_object_comparator_t 详解C14 透明比较器如何避免键查询的字符串构造【免费下载链接】jsonJSON for Modern C项目地址: https://gitcode.com/GitHub_Trending/js/jsondefault_object_comparator_t是 nlohmann::basic_json即 JSON for Modern C用来声明 JSON 对象键比较器的默认类型别名。它直接影响对象的键查找、at()/operator[]/value()等按键访问接口的可用性以及在 C14 及以上标准下能否以std::string_view等非std::string类型直接查询键。本文以该类型的 API 文档为主体结合仓库源码讲清楚它的定义、C14 前后两种取值的差异、实际比较器类型的确定方式以及它在 API 层如何被消费。类型定义C14 前后两个版本basic_json类内部提供了如下的类型别名using default_object_comparator_t std::lessStringType; // until C14 using default_object_comparator_t std::less; // since C14即在 C14 之前的编译环境中它是绑定到字符串键类型StringType默认即std::string的std::lessStringType自 C14 起它被切换为透明比较器std::less空模板参数形式Heterogeneous Lookup 比较器。在源码 include/nlohmann/json.hpp 中可以看到对应的实现/// brief default object key comparator type /// The actual object key comparator type (ref object_comparator_t) may be /// different. #if defined(JSON_HAS_CPP_14) // use of transparent comparator avoids unnecessary repeated construction of temporaries // in functions involving lookup by key with types other than object_t::key_type (aka. StringType) using default_object_comparator_t std::less; #else using default_object_comparator_t std::lessStringType; #endif源码注释明确点出了切换动机“使用透明比较器可以避免在按键查找时反复构造临时对象——尤其是查找键的类型与object_t::key_type即StringType不同的场景”。default_object_comparator_t是object_t的第三个模板实参因此默认情况下对象容器为// until C14 std::mapstd::string, basic_json, std::lessstd::string, std::allocatorstd::pairconst std::string, basic_json // since C14 std::mapstd::string, basic_json, std::less, std::allocatorstd::pairconst std::string, basic_json完整定义见 include/nlohmann/json.hppusing object_t ObjectTypeStringType, basic_json, default_object_comparator_t, AllocatorTypestd::pairconst StringType, basic_json;透明比较器带来的实际收益C14 之前std::lessstd::string只能比较两个std::string。这意味着当用户拿着std::string_view、字符串字面量const char*或宽字符串等去查询对象键时库必须先把它们构造成std::string再传入查找函数每一次查找都可能伴随一次字符串分配。C14 引入的std::lessheterogeneous 比较器没有绑定具体类型其operator()接受任意可比较的左右操作数。由于底层容器std::map从 C14 起支持 Heterogeneous Lookup键查找可以直接使用原始类型与树中已有的const std::string键做比较从而省去临时std::string的构造。这正是文档中所述 “Since C14 a transparent comparator is used which prevents unnecessary string construction when looking up a key in an object” 的含义。这一收益在库的 API 层被系统性利用basic_json的value()等接口依据“比较器是否透明”提供了两组重载——透明比较器版本接受KeyType 泛型键如string_view非透明版本只接受const typename object_t::key_type。相关重载的 SFINAE 条件可见 include/nlohmann/json.hpp。一个典型的 C17 用法示例来自 docs/mkdocs/docs/examples/at__keytype.c17.cpp展示了std::string_view直接作为键参与at()查询与写入json object { {the good, il buono}, {the bad, il cattivo}, {the ugly, il brutto} }; // 使用 string_view 输出键 the ugly 对应的元素 std::cout object.at(the uglysv) \n; // 使用 string_view 修改键 the bad 对应的元素 object.at(the badsv) il cattivo;在 C14 及以后的编译环境下由于比较器是透明的the uglysv无需先转换为std::string即可完成树中查找。实际生效的比较器object_comparator_t 的确定逻辑文档特别指出default_object_comparator_t只是默认比较器实际生效的比较器取决于object_t并通过object_comparator_t获取using object_comparator_t typename object_t::key_compare; // or using object_comparator_t default_object_comparator_t;即如果object_t带有key_compare成员如std::map则以typename object_t::key_compare为准否则回退到default_object_comparator_t。源码中的选择逻辑位于 include/nlohmann/detail/meta/type_traits.hpp// obtains the actual object key comparator templatetypename BasicJsonType struct actual_object_comparator { using object_t typename BasicJsonType::object_t; using object_comparator_t typename BasicJsonType::default_object_comparator_t; using type typename std::conditionalhas_key_compareobject_t::value, typename object_t::key_compare, object_comparator_t::type; }; templatetypename BasicJsonType using actual_object_comparator_t typename actual_object_comparatorBasicJsonType::type;而basic_json中object_comparator_t正是这样派生出来的include/nlohmann/json.hppusing object_comparator_t detail::actual_object_comparator_tbasic_json;对于默认配置object_t为std::mapkey_compare就是default_object_comparator_t本身两者一致但当用户自定义ObjectType并指定了自己的比较器时object_comparator_t会如实反映该比较器default_object_comparator_t则保持为库的默认值不变。透明性检测与键类型的 SFINAE 约束库内判断“当前比较器是否透明”的类型特征是 include/nlohmann/detail/meta/type_traits.hpp 中的is_transparent// checks whether T has a member named is_transparent templatetypename T struct is_transparent : bool_constantimpl::is_transparentT() {};其实现通过is_detecteddetect_is_transparent, T检测比较器类型是否带有is_transparent成员std::less有is_transparent truestd::lessstd::string没有。在此基础上is_usable_as_key_type等特征include/nlohmann/detail/meta/type_traits.hpp进一步约束了哪些类型可以被用作键templatetypename Comparator, typename ObjectKeyType, typename KeyTypeCVRef, bool RequireTransparentComparator true, bool ExcludeObjectKeyType RequireTransparentComparator, typename KeyType uncvref_tKeyTypeCVRef using is_usable_as_key_type typename std::conditional is_comparableComparator, ObjectKeyType, KeyTypeCVRef::value !(ExcludeObjectKeyType std::is_sameKeyType, ObjectKeyType::value) (!RequireTransparentComparator || is_detecteddetect_is_transparent, Comparator::value) !is_json_pointerKeyType::value, std::true_type, std::false_type::type;其中is_comparable同文件 L707-L731会验证“比较器能否同时以两种参数顺序比较ObjectKeyType与KeyType”这正是 Heterogeneous Lookup 所需的对称可比较性。由此可见透明比较器不只是一个性能优化它还决定了泛型键string_view等能否参与键类型相关的编译期检查。官方示例直接观察比较器的行为API 文档给出的示例程序 docs/mkdocs/docs/examples/default_object_comparator_t.cpp 直接实例化default_object_comparator_t并比较两个字符串键#include iostream #include nlohmann/json.hpp using json nlohmann::json; int main() { std::cout std::boolalpha one two : json::default_object_comparator_t{}(one, two) \n three four : json::default_object_comparator_t{}(three, four) std::endl; }其输出docs/mkdocs/docs/examples/default_object_comparator_t.outputone two : true three four : false示例印证了两点default_object_comparator_t是一个可以直接默认构造并调用的函数对象语义即字符串的字典序“小于”比较按字符的字典序进行three与four比较时t f结果为false与std::map以键的字典序组织节点的行为一致。对对象行为的连带影响比较器是object_t的核心组成部分因此默认比较器也决定了 JSON 对象的多项行为详见 object_t 文档键排序与序列化顺序由于默认object_t是std::map且以std::less系列比较器排序name/value 对按键的字典序存储dump()序列化也按此顺序输出。例如{b: 1, a: 2}会被存储并序列化为{a: 2, b: 1}。对象相等性与遍历顺序无关对象比较时 name/value 对的顺序不影响结果{b: 1, a: 2}与{a: 2, b: 1}相等符合 RFC 8259 对 JSON 对象“无序”的描述。重复键的解析行为键名不唯一时最终保留哪个值是未指定行为若要拒绝重复键文档指向了解析回调方案见 解析器回调文档 中 “Rejecting duplicate object keys” 一节的示例 docs/mkdocs/docs/examples/reject_duplicate_keys.cpp。键顺序不被保留std::mapstd::less的默认组合意味着遍历对象会按字母序而非插入序进行。若需保留插入顺序可改用ordered_json/ordered_map参见 ordered_json 文档 与 docs/mkdocs/docs/examples/ordered_json.cpp。版本历史与自定义比较器的注意事项default_object_comparator_t类型别名自3.11.0版本引入在此之前比较器类型仅隐含体现在object_t的定义中。相关变更object_comparator_t自3.11.0起改为条件定义为typename object_t::key_compare可用时或default_object_comparator_t否则。从源码结构看比较器透明性还约束了 API 能力边界当用户为basic_json自定义了一个非透明的ObjectType/比较器时依赖detail::is_transparentobject_comparator_t::value的泛型键重载如接受string_view的value()重载见 include/nlohmann/json.hpp将不再参与重载决议按键查找会退回到必须提供object_t::key_type类型的接口。换句话说想要继续使用std::string_view之类的轻量键类型进行查找自定义比较器必须满足透明比较器的要求。小结default_object_comparator_t虽只是一个类型别名却是 JSON 对象键访问行为的关键枢纽C14 之前定义为std::lessStringTypeC14 及以后定义为透明比较器std::less后者使std::string_view等非字符串键类型可以直接参与对象查找避免临时std::string的构造实际生效的比较器由object_t::key_compare决定通过object_comparator_t源码中的actual_object_comparator_t获取比较器是否透明is_transparent特征直接参与value()、at()等接口重载的 SFINAE 选择决定了泛型键查找能力是否可用默认比较器与std::map的组合带来了字典序存储/序列化、顺序无关的对象相等性等默认行为这些均可通过object_t的类型特化如ordered_json改变。【免费下载链接】jsonJSON for Modern C项目地址: https://gitcode.com/GitHub_Trending/js/json创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →