I built a simple JSON encoder/decoder in C (I know there are many excellent solutions for this problem out there; I'm learning the language, and it's simply a challenge to myself, and I'm not about to pretend that my solution is any good).
Initially, I had the following two operator []
overloads:
json& operator[](const std::string& key) {
assert_type_valid(/* "object" type */);
return (*object_data_)[key];
}
json& operator[](size_t index) {
assert_type_valid(/* "array" type */);
return (*array_data_)[index];
}
...
std::unique_ptr<std::unordered_map<std::string, json>> object_data_;
std::unique_ptr<std::vector<json>> array_data_;
And this worked great! I could do something like my_json["someKey"]["someOtherKey"][4]["thirdKey"]
to access nested values.
But then, because my project uses a bunch of Windows APIs that make use of both char
/std::string
and wchar_t
/std::wstring
, I wanted to make both json
and wjson
types, so I turned my existing json
class and parser into a basic_json<CharType, StringType>
.
Now, the same code looks like:
// Using C for CharType and S for StringType for brevity here...
basic_json<C, S>& operator[](const S& key) {
assert_type_valid(/* "object" type */);
return (*object_data_)[key];
}
basic_json<C, S>& operator[](size_t index) {
assert_type_valid(/* "array" type */);
return (*array_data_)[index];
}
...
std::unique_ptr<std::unordered_map<S, basic_json<C, S>>> object_data_;
std::unique_ptr<std::vector<basic_json<C, S>>> array_data_;
Attempting to use either operator []
now results in this error:
error C2666: 'basic_json<char,std::string>::operator []': 2 overloads have similar conversions
could be ...::operator [](size_t)
or ...::operator [](const S &)
or built-in C operator[(__int64, const char [8])
I've tried adding a third operator [](const C* key)
overload, but that hasn't made a difference.
My actual question: Why has templating caused this?
I'm under the impression that template types are determined at compile time, so presumably the compiler should understand what C
and S
are and be able to know that I'm not passing in a size_t
when I do my_json["someKey"]
.
Also, I feel like templating has turned the once-pretty code into a huge mess. Is it ever worth simply duplicating a bunch of code in a situation like this? Like having a second parser that just works explicitly with wchar_t
and std::wstring
?
CodePudding user response:
"Why has templating caused this?" Because class template members are only instantiated when necessary.
The clue appears to be in the operator[] which you did not mention:
built-in C operator[(__int64, const char [8])
.
That is to say, the compiler considers 5["someKey"]
. Wait, what? Yes, in C that is valid. It's the same as "someKey"[5]
, for compatibility with C.
Now how is that even relevant? Your basic_json<C, S>
has an implicit conversion to int
and std::string
has an implicit conversion from const char [8]
, so the two overloads are equal.
The fix: remove the implicit conversion to int
.