#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?
Operator Overloading in C++ This means C++ has the ability to provide the operators with a special meaning for a data type, this ability is known as operator overloading. For example, we can overload an operator '+' in a class like String so that we can concatenate two strings by just using +.
Operators play an important role in computer programming. By using it, you can change the behavior of an operator based on the types of its arguments.
Function overloading means using a single name and giving more functionality to it. Operator overloading means adding extra functionality for a certain operator. When an operator is overloaded, the operator has different meanings, which depend on the type of its operands.
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
.
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 enum
s which cannot exist, whose only purpose is to make sure that the two templates differ in sufficient ways for the compiler not to complain.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With