Home > Blockchain >  Template class: operator []: 2 overloads have similar conversions
Template class: operator []: 2 overloads have similar conversions

Time:10-20

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.

  • Related