Pratik Deoghare
Pratik Deoghare

Reputation: 37172

Strange operator overloading behavior?

#include <iostream>
using namespace std;

class Foo{

        string _s;
        public:
        Foo(string ss){
                _s = ss;
        }
        Foo& operator=(bool b){
                cout << "bool" << endl;
                return *this;
        }
        Foo& operator=(const string& ss){
                cout << "another one" << endl;
                return *this;
        }
};


int main(){

        Foo f("bar");
        f = "this";
        return 0;

}

I have overloaded = operator. I expected f = "this"; statement to call operator=(const string& ss) overload. But it does not. It calls operator=(bool b) overload. Why?

Upvotes: 7

Views: 208

Answers (4)

Vlad Ellis
Vlad Ellis

Reputation: 13

As others have said, an easy fix to this is to simply cast your string to an std::string, when doing the operator, so C++ knows exactly which overload to choose:

#include <iostream>
using namespace std;

class Foo{

        string _s;
        public:
        Foo(string ss){
                _s = ss;
        }
        Foo& operator=(bool b){
                cout << "bool" << endl;
                return *this;
        }
        Foo& operator=(const string& ss){
                cout << "another one" << endl;
                return *this;
        }
};


int main(){

        Foo f((string)"bar");
        f = (string)"this";
        return 0;

}

Upvotes: 0

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275330

As noted by another answer, the pointer-to-char to bool conversion is preferred because it involves no user-defined conversions, while the std::string conversion has a user-defined conversion in it.

C++11 offers the ability to do manual type control.

template<size_t n>
struct enumarated_enum {
private:
  enum empty {};
};

template<bool b, size_t n=0>
using EnableIf = typename std::enable_if< b, typename enumerated_enum<n>::empty >::type;

template<typename String, EnableIf< std::is_convertible< String, std::string >::value >... >
Foo& operator=(String&& s) {
  cout << "string overload" << "\n";
}

// either this:
template<typename Bool, EnableIf< !std::is_convertible< Bool, std::string >::value && std::is_convertible< Bool, bool >::value, 1 >... >
Foo& operator=(Bool&& b) {
  cout << "bool overload" << "\n";
}
// or this:
Foo& operator=(bool b) {
  cout << "bool overload" << "\n";
}

where we match a type perfectly if it can be converted to a std::string, and the template doesn't match if it cannot be converted to std::string and other overloads are looked at.

If you have many types you want to be able to support, you have to use long logical forms that describe which of the overloads you want to run, and make sure the others don't accept it (with the not construct above). If you only have two types, the second type can be a traditional overload. The template overload gets first crack at anything that doesn't match the traditional overload exactly...

(The second template argument is a variardic list of numbered enums which cannot exist, whose only purpose is to make sure that the two templates differ in sufficient ways for the compiler not to complain.)

Upvotes: 1

juanchopanza
juanchopanza

Reputation: 227390

This operator operator=(const string& ss) requires a conversion of a user defined type for the argument (const char* to std::string), whereas the bool version has none and so provides a better match: you get the conversions from built-in types const char[5] to const char* to bool.

Upvotes: 13

OriginalCliche
OriginalCliche

Reputation: 401

Without delving into C++ standards, on the surface your problem is, you defined your assignment overload with string& and a bool but in your test you're assigning a char* array.

So if you define a string and assign, it will work as expected.

Check it working here : http://codepad.org/owb6noXR

Upvotes: 0

Related Questions