可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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
回答1:
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*
.
回答2:
$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
回答3:
There's no exact std::string::operator=. The candidates are, paraphrased,
s = (const std::string)(std::string)t;
s = (const char*)t;
s = (char)(int)t;
I think things will work if you change it to return const std::string. (EDIT: I'm wrong.) Also note that the first function should return const char *. If you need to cast a string literal to char*, you're doing something wrong; string literals are not writeable.
回答4:
Actually, it's because std::string
offers an assignment operator that takes a const char*
.
回答5:
Alright, thanks a lot already everyone. I think I am starting to get the hang of it, kind of...
First of all I wasn't aware of the fact, that char is just an 8-bit int. Thanks for that clarification.
So I understand that, because there are three assignment operators defined for std::string, each with different argument (string, char*, const char*) the right-hand-side of my expression
s=t
doesn't know, which type is has to convert into, since there are multiple, potentially matching (for this assignment to std::string) conversions defined with either
operator int () {return 77;};
operator std::string () {return "hello";};
(since char : 8bit int)
or
operator char* () {return (char*)"hi";};
operator std::string () {return "hello";};
Is that right? So in idiots terms, the left-hand-side of the assignment isn't telling the right-hand-side which type it expects, so rhs has to choose from its options, where one is as good as some other? std::string operator= is being to tolerant for my intents?
So far so good, I thought I got it - but then, why does the following create ambiguity as well?
using namespace std;
#include <string>
#include <iostream>
class testClass
{
public:
operator float () {return float(77.333);};
operator std::string () {return "hello";};
};
int main()
{
std::string s = "goodday";
testClass t;
s = t;
cout<< " string: "<<s <<endl;
return 0;
}
Now there is only one matching conversion operator defined by me, right?
std::string operator= cannot take floats, or can it? Or is float in some way equivalent to some variant of char again?
I understand the code as 's=' telling the rhs: "give me a string, char* or const char*"
Rhs checks what it can provide given an instance of testClass, and the only match is testClass::operator std::string
Again, thanks for your patience, expertise and time guys - I really appreciate it.