Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ overloading conversion operator for custom type to std::string

I hope someone might be able to answer why the following doesn't work. Bear with me though, I am still very much a noob... I just cannot get to the bottom of why the following

using namespace std;
#include <string>
#include <iostream>

class testClass
{
public:
 operator char* () {return (char*)"hi";};
 operator int ()  {return 77;};
 operator std::string  () {return "hello";};
};

int main()
{
 char* c;
 int i;
 std::string s = "goodday";

 testClass t;

 c = t;
 i = t;
 s = t;

 cout<< "char: " << c << " int: " << i << " string: "<<s<<endl;

 return 0;
}

gives me a compile time error:

myMain.cpp: In function ‘int main()’:
myMain.cpp:23: error: ambiguous overload for ‘operator=’ in ‘s = t’
/usr/include/c++/4.2.1/bits/basic_string.h:500: note: candidates are: std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(const std::basic_string<_CharT, _Traits, _Alloc>&) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]
/usr/include/c++/4.2.1/bits/basic_string.h:508: note:                 std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(const _CharT*) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]
/usr/include/c++/4.2.1/bits/basic_string.h:519: note:                 std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(_CharT) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]

If I do not attempt the assignment

s = t;

it does work.

I've been trying for hours to even comprehend the error message, but what's puzzling me most is that is does work for char*.

I'm grateful for any hint. Thanks! Markus

like image 959
Markus Avatar asked Aug 19 '10 01:08

Markus


People also ask

How do you do custom operator overloading?

To overloading a prefix operator, we need to add a prefix keyword before a func . In the following example, I overload the - unary operator for a string, which will reverse the characters in a given string. <1> We add the prefix keyword to tell the compiler that this is intended to use as a prefix operator.

What is C++ conversion operator?

Conversion Operators in C++ C++ supports object oriented design. So we can create classes of some real world objects as concrete types. Sometimes we need to convert some concrete type objects to some other type objects or some primitive datatypes. To make this conversion we can use conversion operator.

What is operator overloading explain conversion?

The operator overloading defines a type conversion operator that can be used to produce an int type from a Counter object. This operator will be used whenever an implicit or explict conversion of a Counter object to an int is required. Notice that constructors also play a role in type conversion.

Can operator be overloaded in C?

C does not support operator overloading (beyond what it built into the language).


2 Answers

What the error is trying to explain is that your assignment "s = t", where s is a std::string, would be valid if t were a std::string too, or if t were a [const] char*. Your conversion operators can convert a t into either, so the compiler has no basis on which to choose one over the other....

You can disambiguate this explicitly by selecting the conversion you want:

s = t.operator std::string();
s = static_cast<std::string>(t);

Or you can provide only one of the conversions and let the user do a further conversion when necessary.

You may find though - in the end - that any conversion operator is more trouble than it's worth... it's telling that std::string itself doesn't provide a conversion operator to const char*.

like image 113
Tony Delroy Avatar answered Sep 19 '22 08:09

Tony Delroy


$13.3.1.5/2 states- "The conversion functions of S and its base classes are considered. Those that are not hidden within S and yield type T or a type that can be converted to type T via a standard conversion sequence (13.3.3.1.1) are candidate functions. Conversion functions that return a cv-qualified type are considered to yield the cv-unqualified version of that type for this process of selecting candidate functions. Conversion functions that return “reference to cv2 X” return lvalues of type “cv2 X” and are therefore considered to yield X for this process of selecting candidate functions."

The assignment s = t works as follows:

a) All members in the type of 't' (testClass) are considered which can convert 't' to 's'.

Candidate 1: operator string();   // s created using member string::operator=(string const&)
Candidate 2: operator char *()    // s created using member string::operator=(char const*)
Candidate 3: operator char *()    // s created using member string::operator=(char *)

b) All of the above candidates are viable (that is, in absence of other candidates, the compiler can successfully resolve the function call to either of them)

c) However, now the best viable candidate has to be determined. The Conversion sequences involved are:

Candidate 1: U1 : operator string()
Candidate 2: U2 : operator char*()->const qualification to match string::operator=(char const*)
Candidate 3: U3 : operator char*()

$13.3.3.1.1/3 states - "The rank of a conversion sequence is determined by considering the rank of each conversion in the sequence and the rank of any reference binding (13.3.3.1.4). If any of those has Conversion rank, the sequence has Conversion rank;"

This means that U1, U2 and U3 are all having Conversion rank and at a first level neither s better than the other. However, the standard also states

User-defined conversion sequence U1 is a better conversion sequence than another user-defined conversion sequence U2 if they contain the same user-defined conversion function or constructor and if the second standard conversion sequence of U1 is better than the second standard conversion sequence of U2.

So, let's see what this means.

Between U1 and U2, they involve, different conversion functions and hence none is better than the other

Between U1 and U3, they involve, different conversion functions and hence none is better than the other

So what about U1 and U2. They involve the same conversion function, which satisfies the first part of the "and" condition above

So what about the part "and if the second standard conversion sequence of U1 is better than the second standard conversion sequence of U2."

In U2, the second standard conversion sequence requires a const qualification, where in U3 this is not required. The second standard conversion sequence of U3 is an Exact Match.

But as Table 9 in the Standard states, CV qualification is also considered to be an Exact Match.

Therefore U2 and U3 are also really indistinguisable as far as overload resolution is considered.

This means U1, U2 and U3 are all really as good as each other and the compiler finds resolving the call(as part of assignment statement) as ambiguous, as there is no unambiguous best viable function

like image 43
Chubsdad Avatar answered Sep 22 '22 08:09

Chubsdad