Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ addition overload ambiguity

I am coming up against a vexing conundrum in my code base. I can't quite tell why my code generates this error, but (for example) std::string does not.

class String {
public:
    String(const char*str);
    friend String operator+ ( const String& lval, const char *rval );
    friend String operator+ ( const char *lval, const String& rval );
    String operator+ ( const String& rval );
};

The implementation of these is easy enough to imagine on your own.

My driver program contains the following:

String result, lval("left side "), rval("of string");
char lv[] = "right side ", rv[] = "of string";
result = lv + rval;
printf(result);
result = (lval + rv);
printf(result);

Which generates the following error in gcc 4.1.2:

driver.cpp:25: error: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
String.h:22: note: candidate 1: String operator+(const String&, const char*)
String.h:24: note: candidate 2: String String::operator+(const String&)

So far so good, right? Sadly, my String(const char *str) constructor is so handy to have as an implicit constructor, that using the explicit keyword to solve this would just cause a different pile of problems.

Moreover... std::string doesn't have to resort to this, and I can't figure out why. For example, in basic_string.h, they are declared as follows:

template<typename _CharT, typename _Traits, typename _Alloc>
basic_string<_CharT, _Traits, _Alloc>
operator+(const basic_string<_CharT, _Traits, _Alloc>& __lhs,
          const basic_string<_CharT, _Traits, _Alloc>& __rhs)

template<typename _CharT, typename _Traits, typename _Alloc>
basic_string<_CharT,_Traits,_Alloc>
operator+(const _CharT* __lhs,
          const basic_string<_CharT,_Traits,_Alloc>& __rhs);

and so on. The basic_string constructor is not declared explicit. How does this not cause the same error I'm getting, and how can I achieve the same behavior??

like image 299
Nate Avatar asked Feb 18 '26 06:02

Nate


1 Answers

The reason for the ambiguity is that one candidate function is better than another candidate function only if none of its parameters are a worse match than the parameters of the other. Consider your two functions:

friend String operator+(const String&, const char*); // (a)
String operator+(const String&);                     // (b)

You are calling operator+ with a String and a const char*.

The second argument, of type const char*, clearly matches (a) better than (b). It is an exact match for (a), but a user-defined conversion is required for (b).

Therefore, in order for there to be an ambiguity, the first argument must match (b) better than (a).

The String on the left-hand side of the call to operator+ is not const. Therefore, it matches (b), which is a non-const member function, better than (a), which takes a const String&.

Therefore, any of the following solutions would remove the ambiguity:

  • Change the member operator+ to be a const member function
  • Change the non-member operator+ to take a String& instead of a const String&
  • Call operator+ with a const String on the left hand side

Obviously, the first, also suggested by UncleBens, is the best way to go.

like image 195
James McNellis Avatar answered Feb 19 '26 19:02

James McNellis



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!