The code below (Goldbolt) compiles and runs (on both gcc and clang) and does what I would hope. But I'm surprised! I expected to have to use an adl_serializer specialisation (as opposed to the hidden friend here) for it to be able to find the to_json
/from_json
functions, as the Example
class is hidden inside a std::shared_ptr
.
So the question is, is this expected (and why?), or a bug in the compilers that might break my code when it gets fixed.
#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);
}
A friend function definition not declared elsewhere is declared in the same namespace as the class it is defined in.
There are two reasons your to_json
and from_json
are found.
The first, and simplest reason is that Example
is in the global namespace, so lookup will reach there if it doesn't find a match elsewhere.
The second reason is that adl looks at the namespace that Example
is in.
For arguments whose type is a class template specialization, in addition to the class rules, the following types are examined and their associated classes and namespaces are added to the set
a. The types of all template arguments provided for type template parameters (skipping non-type template parameters and skipping template template parameters)
(from cppreference)