Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Template class: operator []: 2 overloads have similar conversions

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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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?

>Solution :

"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.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading