 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Lucian Radu Teodorescu Guest
|
Posted: Thu Aug 10, 2006 5:36 pm Post subject: User defined conversion ambiguity |
|
|
Hello all
Should the following code compile according to the standard? I have
different results with different compilers.
Thank you,
Lucian Radu Teodorescu
#include <iostream>
using namespace std;
struct Target;
struct Source;
struct Target
{
Target() {}
Target(const Source& other)
{
cout << "Target: conversion constructor" << endl;
}
};
struct Source
{
Source() {}
operator Target ()
{
cout << "Source: conversion operator" << endl;
return Target();
}
};
int main(int argc, char* argv[])
{
Target t;
Source s;
t = s;
return 0;
}
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
alex Guest
|
Posted: Fri Aug 11, 2006 5:13 pm Post subject: Re: User defined conversion ambiguity |
|
|
Lucian Radu Teodorescu wrote:
| Quote: | Should the following code compile according to the standard?
|
It should, IMHO.
| Quote: | I have different results with different compilers.
|
What error message(s) do you get?
| Quote: | int main(int argc, char* argv[])
{
Target t;
Source s;
t = s;
return 0;
}
|
This compiles cleanly on my system:
$ g++ -Wall source-target.cpp
$ ./a
Source: conversion operator
$ g++ -v
Reading specs from ...
[skipped]
Thread model: win32
gcc version 3.3.1 (mingw special 20030804-1)
$
Source::operator Target() is called. But did you really expect a call
to Target's constructor in the 't=s' line?
Thanks.
Alex
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Jeffrey Schwab Guest
|
Posted: Fri Aug 11, 2006 8:03 pm Post subject: Re: User defined conversion ambiguity |
|
|
Lucian Radu Teodorescu wrote:
| Quote: | Hello all
Should the following code compile according to the standard? I have
different results with different compilers.
Thank you,
Lucian Radu Teodorescu
#include <iostream
using namespace std;
struct Target;
struct Source;
struct Target
{
Target() {}
Target(const Source& other)
{
cout << "Target: conversion constructor" << endl;
}
};
struct Source
{
Source() {}
operator Target ()
{
cout << "Source: conversion operator" << endl;
return Target();
}
};
int main(int argc, char* argv[])
{
Target t;
Source s;
t = s;
return 0;
}
|
The compiler's not sure whether to use the Target constructor or the
conversion operator. Remove one or the other.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Lucian Radu Teodorescu Guest
|
Posted: Mon Aug 14, 2006 5:45 pm Post subject: Re: User defined conversion ambiguity |
|
|
alex wrote:
| Quote: | Lucian Radu Teodorescu wrote:
Should the following code compile according to the standard?
It should, IMHO.
I have different results with different compilers.
What error message(s) do you get?
|
with VC++ 2005 i get on the "t = s" line:
d:\work\test\test\test.cpp(34) : error C2679: binary '=' : no operator
found which takes a right-hand operand of type 'Source' (or there is no
acceptable conversion)
d:\work\test\test\test.cpp(16): could be 'Target
&Target::operator =(const Target &)'
while trying to match the argument list '(Target, Source)'
I've also tried the code on Drinkumware Exam (online compilation) and
here are the results:
VC++ 8: Fail
VC++ 7.1: Fail
VC++ 6: Fail
EDG/C++: Success
MINGW: Success with warnings:
"sourceFile.cpp: In
function `int main(int, char**)':
sourceFile.cpp:35: warning: choosing
`Source::operator Target()' over `Target::Target(const Source&)'
sourceFile.cpp:35: warning:
for conversion from `Source' to `Target'
sourceFile.cpp:35: warning:
because conversion sequence for the argument is better"
| Quote: | Source::operator Target() is called. But did you really expect a call
to Target's constructor in the 't=s' line?
|
I didn't find in the standard very clearly if there is an ambiguity or
not. And if there is no ambiguity what conversion path is choosen.
Thank you
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Martin Bonner Guest
|
Posted: Mon Aug 14, 2006 5:46 pm Post subject: Re: User defined conversion ambiguity |
|
|
alex wrote:
| Quote: | Lucian Radu Teodorescu wrote:
Should the following code compile according to the standard?
|
[Snip classes with both T.T(const S &), and S.operator T() defined and
then ...]
| Quote: | Target t;
Source s;
t = s;
It should, IMHO.
|
Why? It looks ambigous to me
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Jeffrey Schwab Guest
|
Posted: Thu Aug 17, 2006 8:23 pm Post subject: Re: User defined conversion ambiguity |
|
|
kanze wrote:
| Quote: | Martin Bonner wrote:
alex wrote:
Lucian Radu Teodorescu wrote:
Should the following code compile according to the standard?
[Snip classes with both T.T(const S &), and S.operator T() defined and
then ...]
Target t;
Source s;
t = s;
It should, IMHO.
Why? It looks ambigous to me
I don't think so. All other things being equal, the compiler
should prefer to pass a non-const object to a non-const
reference or call a non-const function on it. In this case, all
other things are equal, so the compiler should prefer the
non-const operator Target() function to binding s to the S
const& parameter of the constructor.
|
User-defined conversions are considered only if they are necessary to
resolve a call.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
kanze Guest
|
Posted: Fri Aug 18, 2006 6:37 pm Post subject: Re: User defined conversion ambiguity |
|
|
Jeffrey Schwab wrote:
| Quote: | kanze wrote:
Martin Bonner wrote:
alex wrote:
Lucian Radu Teodorescu wrote:
Should the following code compile according to the standard?
[Snip classes with both T.T(const S &), and S.operator T() defined and
then ...]
Target t;
Source s;
t = s;
It should, IMHO.
Why? It looks ambigous to me
I don't think so. All other things being equal, the compiler
should prefer to pass a non-const object to a non-const
reference or call a non-const function on it. In this case, all
other things are equal, so the compiler should prefer the
non-const operator Target() function to binding s to the S
const& parameter of the constructor.
User-defined conversions are considered only if they are
necessary to resolve a call.
|
Which is the case here. The only available operator= requires a
Target on the right hand side, and there is no standard
conversion sequence which will convert a Source to a Target.
The only question is which user-defined conversion to use:
Target::Target(Source const&), or Source::operator Target().
The first binds a non-const object to a const reference, and the
second uses the non-const object to call a non-const function
(which, according to §13.3.1/4, acts exactly as if we were
binding to a non-const reference). As far as I can see, this is
the *only* difference in the two possibilities. And that
difference is covered in §13.3.3.2/3, fifth bullet: Target
const& is more cv-qualified than Target& (the implicit parameter
of Target::operator Target()), so Target& is a better match.
(But I'm not 100% sure that this rule applies here.)
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Lucian Radu Teodorescu Guest
|
Posted: Mon Aug 21, 2006 8:48 pm Post subject: Re: User defined conversion ambiguity |
|
|
kanze wrote:
| Quote: | Which is the case here. The only available operator= requires a
Target on the right hand side, and there is no standard
conversion sequence which will convert a Source to a Target.
The only question is which user-defined conversion to use:
Target::Target(Source const&), or Source::operator Target().
The first binds a non-const object to a const reference, and the
second uses the non-const object to call a non-const function
(which, according to §13.3.1/4, acts exactly as if we were
binding to a non-const reference). As far as I can see, this is
the *only* difference in the two possibilities. And that
difference is covered in §13.3.3.2/3, fifth bullet: Target
const& is more cv-qualified than Target& (the implicit parameter
of Target::operator Target()), so Target& is a better match.
(But I'm not 100% sure that this rule applies here.)
|
I believe that we have the following scenario:
Target::operator = (const Target&) is the only viable function to be
called, and the problem is what conversion is performed to convert
Source to Target.
The difference you spoted is only relevant when the compiler is trying
to distinguish between two viable functions, and not when
distinguishing between two user-defined conversion sequences. If we
have had two candidate functions to distinguish from, according to
13.3.3.1.2/1 and 13.3.3.2/3, this difference regards the initial
standard conversion sequence which is ignored when comparing
user-defined conversion sequences. Besides this the 13.3.3.2/3 rule
applies only if the converions constructor or operator are the same in
both user-defined conversions. I don't think this is our case.
Now I found that 1.3.3.1/10 describes exactly this case: several
different sequences for conversion are found, so this is an ambiguous
conversion sequence. If this is used when selecting the best viable
function, the call will be ill-formed.
Am I correct?
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
kanze Guest
|
Posted: Tue Aug 22, 2006 12:41 am Post subject: Re: User defined conversion ambiguity |
|
|
Lucian Radu Teodorescu wrote:
| Quote: | kanze wrote:
Which is the case here. The only available operator= requires a
Target on the right hand side, and there is no standard
conversion sequence which will convert a Source to a Target.
The only question is which user-defined conversion to use:
Target::Target(Source const&), or Source::operator Target().
The first binds a non-const object to a const reference, and the
second uses the non-const object to call a non-const function
(which, according to §13.3.1/4, acts exactly as if we were
binding to a non-const reference). As far as I can see, this is
the *only* difference in the two possibilities. And that
difference is covered in §13.3.3.2/3, fifth bullet: Target
const& is more cv-qualified than Target& (the implicit parameter
of Target::operator Target()), so Target& is a better match.
(But I'm not 100% sure that this rule applies here.)
I believe that we have the following scenario:
Target::operator = (const Target&) is the only viable function to be
called, and the problem is what conversion is performed to convert
Source to Target.
|
That much is sure.
| Quote: | The difference you spoted is only relevant when the compiler
is trying to distinguish between two viable functions, and not
when distinguishing between two user-defined conversion
sequences. If we have had two candidate functions to
distinguish from, according to 13.3.3.1.2/1 and 13.3.3.2/3,
this difference regards the initial standard conversion
sequence which is ignored when comparing user-defined
conversion sequences. Besides this the 13.3.3.2/3 rule applies
only if the converions constructor or operator are the same in
both user-defined conversions. I don't think this is our case.
|
I'll admit that I'm not at all sure. Overload resolution is one
of the most complex parts of the standard. From what I see:
-- Given that there is only one function which can be called,
we are trying to initialize its const reference parameter.
We start in §8.5.3, and find that "Otherwise [our case], a
temporary of type 'cv1 T1'' is created and initialized from
the initializer expression using the rules for a
non-reference copy initialization."
-- This takes us to §8.5/14, fourth bullet, third sub-bullet:
"Otherwise (i.e., for the remaining copy-initialization
cases), user defined conversion sequences which can convert
from the source type to the destination type or (when a
conversion function is used) to a derived class thereof are
enumerated as described in 13.3.1.4, and the best one is
chosen through overload resolution."
-- This brings us to §13.3.1.4, where it explicitely says that
both converting constructors and conversion functions are
involved. At that point, we have an overload set of two
functions, the constructor, and the conversion operator,
each with a single parameter. Since both are viable, we
skip through §13.3.2.
-- In §13.3.3, we start by finding the best match for the
argument we are given. For both functions, we have a user
defined conversion sequence. (In §13.3.3.1/3, exactly three
conversion sequences are listed. In neither case do we have
an ellipsis conversion sequence, and neither is a standard
conversion sequence. Both are thus user-defined conversion
sequences.
-- According to §13.3.3.1.2, a user-defined conversion sequence
consists of three parts; an initial standard conversion
sequence, the user defined conversion, and a final standard
conversion sequence. In our case, only the first differs.
-- There are some special rules in §13.3.3.1.4 which apply to
reference binding; the implicit first parameter is
assimilated to a reference here, with a few differences (not
relevant in our case). In the end, both functions rank
equal.
-- Which brings us to §13.3.3.2/3, fourth bullet: "S1 and S2
differ only in their qualification conversion and yield
similar types T1 and T2, respectively, and the
cv-qualification signature of type T1 is a proper subset of
the cv-qualification signature of type T2." In the case of
the constructor, the signature is Source const&, and in the
case of the conversion function, the first parameter is
assimilated to Source&. So the conversion function wins.
| Quote: | Now I found that 1.3.3.1/10 describes exactly this case:
several different sequences for conversion are found, so this
is an ambiguous conversion sequence.
|
Two are found. Both are user defined, i.e. functions, so
overload resolution is then applied to them.
| Quote: | If this is used when selecting the best viable function, the
call will be ill-formed.
Am I correct?
|
I don't think so, but I would feel better about it if a real
expert were to pontify. I know that all of §13 is very, very
difficult to interpret.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Lucian Radu Teodorescu Guest
|
Posted: Wed Aug 23, 2006 11:24 pm Post subject: Re: User defined conversion ambiguity |
|
|
| Quote: | I'll admit that I'm not at all sure. Overload resolution is one
of the most complex parts of the standard. From what I see:
-- Given that there is only one function which can be called,
we are trying to initialize its const reference parameter.
We start in §8.5.3, and find that "Otherwise [our case], a
temporary of type 'cv1 T1'' is created and initialized from
the initializer expression using the rules for a
non-reference copy initialization."
-- This takes us to §8.5/14, fourth bullet, third sub-bullet:
"Otherwise (i.e., for the remaining copy-initialization
cases), user defined conversion sequences which can convert
from the source type to the destination type or (when a
conversion function is used) to a derived class thereof are
enumerated as described in 13.3.1.4, and the best one is
chosen through overload resolution."
-- This brings us to §13.3.1.4, where it explicitely says that
both converting constructors and conversion functions are
involved. At that point, we have an overload set of two
functions, the constructor, and the conversion operator,
each with a single parameter. Since both are viable, we
skip through §13.3.2.
-- In §13.3.3, we start by finding the best match for the
argument we are given. For both functions, we have a user
defined conversion sequence. (In §13.3.3.1/3, exactly three
conversion sequences are listed. In neither case do we have
an ellipsis conversion sequence, and neither is a standard
conversion sequence. Both are thus user-defined conversion
sequences.
-- According to §13.3.3.1.2, a user-defined conversion sequence
consists of three parts; an initial standard conversion
sequence, the user defined conversion, and a final standard
conversion sequence. In our case, only the first differs.
-- There are some special rules in §13.3.3.1.4 which apply to
reference binding; the implicit first parameter is
assimilated to a reference here, with a few differences (not
relevant in our case). In the end, both functions rank
equal.
|
Ok ... fine so far
| Quote: | -- Which brings us to §13.3.3.2/3, fourth bullet: "S1 and S2
differ only in their qualification conversion and yield
similar types T1 and T2, respectively, and the
cv-qualification signature of type T1 is a proper subset of
the cv-qualification signature of type T2." In the case of
the constructor, the signature is Source const&, and in the
case of the conversion function, the first parameter is
assimilated to Source&. So the conversion function wins.
|
I agree with you that the *first standard conversion sequence* of the
conversion function is better than the *first standard conversion
sequence * of the constructor. But as you said before "In our case,
only the first differs", and looking to §13.3.3.2/3 second (major)
bullet, I see that only the second standard conversion sequence
matters. So, I don't see the reason why the conversion operator is
choosen over the conversion constructor.
| Quote: | Now I found that 1.3.3.1/10 describes exactly this case:
several different sequences for conversion are found, so this
is an ambiguous conversion sequence.
Two are found. Both are user defined, i.e. functions, so
overload resolution is then applied to them.
If this is used when selecting the best viable function, the
call will be ill-formed.
Am I correct?
I don't think so, but I would feel better about it if a real
expert were to pontify. I know that all of §13 is very, very
difficult to interpret.
|
I'm not good at interpreting the standard, so please bear with me if I
interpreted something wrong from the standard.
Maybe we should move this to comp.std.c++ ....
Thank you very much
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|