为何std::shared_ptr<Example>的隐藏友元to_json/from_json可被nlohmann库识别?
我这段代码在GCC和Clang编译器下均可编译运行且符合预期功能,但令我感到意外:由于Example类被封装在std::shared_ptr中,我原本以为必须使用adl_serializer特化才能让nlohmann::json库找到to_json/from_json函数,而非使用隐藏友元实现。因此我的问题是:这是符合预期的行为吗(原因是什么)?还是编译器的bug,未来修复后会导致代码失效?
#include <memory> #include <nlohmann/json.hpp> struct Example { int a; int b; Example(int a, int b) : a(a), b(b){}; Example(Example&&) = delete; Example& operator=(Example&&) = delete; Example(Example&) = delete; Example& operator=(Example&) = delete; friend void to_json(nlohmann::json& j, const std::shared_ptr<Example>& p) { j = {{"a", p->a}, {"b", p->b}}; } friend void from_json(const nlohmann::json& j, std::shared_ptr<Example>& p) { p = std::make_shared<Example>(j.at("a").get<int>(), j.at("b").get<int>()); } }; int main() { auto j = R"({ "a" : 1, "b" : 2 })"_json; auto c = j.get<std::shared_ptr<Example>>(); auto j2 = nlohmann::json(c); }
这是符合预期的行为,核心原因是C++标准中的参数依赖查找(ADL)规则:
- 当nlohmann::json库尝试解析
std::shared_ptr<Example>类型的序列化/反序列化时,会触发ADL查找对应的to_json/from_json函数。ADL会自动搜索与参数类型关联的所有命名空间——这里既包括std(因为std::shared_ptr属于该命名空间),也包括Example所在的全局命名空间。 - 你在
Example类内部定义的友元函数属于隐藏友元,这类函数不会暴露到全局作用域,只能通过ADL被查找。由于Example在全局命名空间,当ADL查找std::shared_ptr<Example>相关的函数时,会匹配到这个友元函数。 - 这种写法完全遵循C++标准和nlohmann::json库的设计逻辑,并没有依赖编译器bug,未来版本的编译器也不会因为修复问题而导致代码失效。
额外说明:nlohmann::json的序列化机制优先级是ADL查找用户自定义函数 > adl_serializer特化 > 默认实现,所以你的写法完全契合库的设计思路。
内容的提问来源于stack exchange,提问作者tp288843
相关产品推荐
相关产品推荐

