Taming C++ operator=(bool) overload's hunger for implicitly converting T*

Advertisements

I’m having trouble understanding how overloads are resolved for operator=(bool).

I have the following overloads:

    json_variant &operator=(json_variant &&value);
    json_variant &operator=(const json_variant &value);
    json_variant &operator=(bool value);
    json_variant &operator=(double value);
    json_variant &operator=(const std::string &value);
    json_variant &operator=(std::string &&value);
    json_variant &operator=(json_object &&value);
    json_variant &operator=(json_array &&value);

The following operation

    json_variant v;
    v = "abc";

calls the operator=(bool) overload instead of the operator=(const std::string&) overload.

I know there’s an implicit conversion for T* to bool. And there’s also an implicit conversion from const char* to std::string. What I don’t understand, is why this isn’t an ambiguous overload that causes an error.

The result of taking the wrong overload is understandably lethal, and it’s a regrettably easy mistake to make.

I can imagine ugly ways to fix it.

    json_variant &operator=(const char*);
    json_variant &operator=(char*);

    template <typename U>
    json_variant&operator=(U*) {
          static_assert("This probably isn't what you want.");
    }

That operator=(bool) should be so greedy seems like such an ugly behavior that I can’t help thinking I’ve missed something.

>Solution :

One quick and dirty solution that works as far back as C++98 and beyond:

struct foo {
  void operator = (bool arg) { }
  void operator = (const void *ptr); // not implemented
};

int main()
{
  foo f;

  f = false; // ok

  f = "abc"; // link time error
}

We declare an overload which takes a const void * pointer. That is a more specific conversion from T * than bool, so that overload is selected by the unwanted pointer argument.

But we don’t implement this overload, so if it is used, the program won’t link.

My above little program builds if I comment out the f = "abc" assignmet, and it also builds if I instead comment out the const void * overload of the operator =, because then the bool overload is selected by that assignment.

Leave a ReplyCancel reply