 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Dylan Nicholson Guest
|
Posted: Mon Jan 12, 2004 10:26 am Post subject: Compatible types and ternary operator |
|
|
Hi
Just wondering if anyone knew the reasoning behind why compatible
types are required between the two alternatives when using the ternary
operator?
I would have thought it was sufficient that the types were convertible
to the l-value, so the following should (in theory) be perfectly safe:
struct Base { };
struct Dev1 : Base { };
struct Dev2 : Base { };
bool foo();
Base* x = foo() ? new Dev1 : new Dev2;
It's just one of those things that's bugged me for a while, and I
figured there must be a good reason for it.
Dylan
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Ron Natalie Guest
|
Posted: Mon Jan 12, 2004 8:02 pm Post subject: Re: Compatible types and ternary operator |
|
|
"Dylan Nicholson" <wizofaus (AT) hotmail (DOT) com> wrote
| Quote: | Hi
Just wondering if anyone knew the reasoning behind why compatible
types are required between the two alternatives when using the ternary
operator?
I would have thought it was sufficient that the types were convertible
to the l-value, so the following should (in theory) be perfectly safe:
|
No, it doesn't try to hunt down a common convertible type. All it
does (it's a bit more involved than this but this is the essence) see
if it can convert the first to the second and the second to the first.
If only one of those conversions is valid, then the converted type is
the value of the expression. Otherwise the program is ill-formed.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Ben Hutchings Guest
|
Posted: Mon Jan 12, 2004 8:11 pm Post subject: Re: Compatible types and ternary operator |
|
|
Dylan Nicholson wrote:
| Quote: | Hi
Just wondering if anyone knew the reasoning behind why compatible
types are required between the two alternatives when using the ternary
operator?
I would have thought it was sufficient that the types were convertible
to the l-value, so the following should (in theory) be perfectly safe:
struct Base { };
struct Dev1 : Base { };
struct Dev2 : Base { };
bool foo();
Base* x = foo() ? new Dev1 : new Dev2;
It's just one of those things that's bugged me for a while, and I
figured there must be a good reason for it.
|
There is no requirement that a conditional expression be used as
the right-hand-side of an assignment expression. There are other
ways it can be used, and it would be hard to infer the expected
result type in other cases. The only case where the type of an
expression is inferred from its context is where the expression is
the name of an overloaded function or member function.
Also, note that in C++ (and in standard C) "lvalue" no longer
means an expression that is suitable for putting on the left of an
assignment operator. An lvalue is, to a first approximation, an
expression that evaluates to a function or object that isn't a
temporary (in the context in which it is used).
You can't assign to an lvalue of function type, non-const object
type, or of a user-defined type without an accessible assignment
operator. You an assign to an rvalue of user-defined type if has
an accessible assignment operator.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Craig Tiller Guest
|
Posted: Tue Jan 13, 2004 10:23 am Post subject: Re: Compatible types and ternary operator |
|
|
I can see some use in the behaviour OP posted... any reason why it couldn't
be allowed?
"Ron Natalie" <ron (AT) sensor (DOT) com> wrote
| Quote: |
"Dylan Nicholson" <wizofaus (AT) hotmail (DOT) com> wrote
Hi
Just wondering if anyone knew the reasoning behind why compatible
types are required between the two alternatives when using the ternary
operator?
I would have thought it was sufficient that the types were convertible
to the l-value, so the following should (in theory) be perfectly safe:
No, it doesn't try to hunt down a common convertible type. All it
does (it's a bit more involved than this but this is the essence) see
if it can convert the first to the second and the second to the first.
If only one of those conversions is valid, then the converted type is
the value of the expression. Otherwise the program is ill-formed.
|
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Dylan Nicholson Guest
|
Posted: Tue Jan 13, 2004 10:25 am Post subject: Re: Compatible types and ternary operator |
|
|
"Ron Natalie" <ron (AT) sensor (DOT) com> wrote
| Quote: | "Dylan Nicholson" <wizofaus (AT) hotmail (DOT) com> wrote
Hi
Just wondering if anyone knew the reasoning behind why compatible
types are required between the two alternatives when using the ternary
operator?
I would have thought it was sufficient that the types were convertible
to the l-value, so the following should (in theory) be perfectly safe:
No, it doesn't try to hunt down a common convertible type. All it
does (it's a bit more involved than this but this is the essence) see
if it can convert the first to the second and the second to the first.
If only one of those conversions is valid, then the converted type is
the value of the expression. Otherwise the program is ill-formed.
I'm aware of *what* the rule is, just wondering historically why it is |
that way. It seems an odd sort of limitation. I can't think of any
obvious reason why the ternary operator should be treated as anything
other than a short-cut to an if-else.
Dylan
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Dylan Nicholson Guest
|
Posted: Tue Jan 13, 2004 10:30 am Post subject: Re: Compatible types and ternary operator |
|
|
Ben Hutchings <do-not-spam-benh (AT) bwsint (DOT) com> wrote
| Quote: | Dylan Nicholson wrote:
Hi
Just wondering if anyone knew the reasoning behind why compatible
types are required between the two alternatives when using the ternary
operator?
I would have thought it was sufficient that the types were convertible
to the l-value, so the following should (in theory) be perfectly safe:
struct Base { };
struct Dev1 : Base { };
struct Dev2 : Base { };
bool foo();
Base* x = foo() ? new Dev1 : new Dev2;
It's just one of those things that's bugged me for a while, and I
figured there must be a good reason for it.
There is no requirement that a conditional expression be used as
the right-hand-side of an assignment expression. There are other
ways it can be used, and it would be hard to infer the expected
result type in other cases. The only case where the type of an
expression is inferred from its context is where the expression is
the name of an overloaded function or member function.
I suppose I can see that the concept of a 'lazily-evaluted' type is |
not part of C++, even at compile-time. Seeing as there's no
completely obvious straightforward method of defining exactly what the
type of a ternary expression result is unless both types are
inter-convertible, it's probably better the way it is. From a
programmer point of view it's a pretty minor inconvenience.
A related question - is there a type of cast in C++ that is always
safe?
eg.
Base* x = foo() ? safe_cast<Base*>(new Dev1) : safe_cast<Base*>(new
Dev2);
I guess what I'm looking for is a cast that only compiles if an
implicit conversion or cast operator is available.
Dylan
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Ben Hutchings Guest
|
Posted: Tue Jan 13, 2004 3:21 pm Post subject: Re: Compatible types and ternary operator |
|
|
Dylan Nicholson wrote:
<snip>
| Quote: | A related question - is there a type of cast in C++ that is always
safe?
|
That depends on what you mean by safe. ;-)
| Quote: | eg.
Base* x = foo() ? safe_cast<Base*>(new Dev1) : safe_cast<Base*>(new
Dev2);
I guess what I'm looking for is a cast that only compiles if an
implicit conversion or cast operator is available.
|
No, there isn't. It has been proposed before as "implicit_cast" or
"safe_cast" but didn't make it into the last standard. You can
define a somewhat useful version as a function template:
template<typename T>
inline T std_convert(T value)
{
return value;
}
Note that the function doesn't really do any conversion.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Wed Jan 14, 2004 10:10 am Post subject: Re: Compatible types and ternary operator |
|
|
Ben Hutchings <do-not-spam-benh (AT) bwsint (DOT) com> wrote
| Quote: | Dylan Nicholson wrote:
Just wondering if anyone knew the reasoning behind why compatible
types are required between the two alternatives when using the
ternary operator?
I would have thought it was sufficient that the types were
convertible to the l-value, so the following should (in theory) be
perfectly safe:
|
I don't understand the business about l-value. What is required (more
or less) is that one of the types be convertable to the other.
| Quote: | struct Base { };
struct Dev1 : Base { };
struct Dev2 : Base { };
bool foo();
Base* x = foo() ? new Dev1 : new Dev2;
It's just one of those things that's bugged me for a while, and I
figured there must be a good reason for it.
There is no requirement that a conditional expression be used as the
right-hand-side of an assignment expression. There are other ways it
can be used, and it would be hard to infer the expected result type in
other cases. The only case where the type of an expression is
inferred from its context is where the expression is the name of an
overloaded function or member function.
|
That's not needed, however. While it would certainly be more difficult
for compiler writers (and more difficult to specify for the authors of
the standard), it would be possible to say something along the lines
that if both E1 and E2 have (possibly const-qualified) pointer to class
types, and both classes have a common base, the highest common base is
the common type. Of course, you would then have to specify things like
what happens in the case of multiple inheritance, etc. And what happens
with lvalues and references :
Dev1 d1 ;
Dev2 d2 ;
Base& b = foo() ? d1 : d2 ;
IMHO, it would be a useful extension. But not easy to correctly
specify, and probably not very easy for compiler writers to implement.
--
James Kanze GABI Software mailto:kanze (AT) gabi-soft (DOT) fr
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Frank Birbacher Guest
|
Posted: Wed Jan 14, 2004 10:27 am Post subject: Re: Compatible types and ternary operator |
|
|
Hi!
Ben Hutchings wrote:
| Quote: | template<typename T
inline T std_convert(T value)
{
return value;
}
Note that the function doesn't really do any conversion.
|
Which could be changed:
template
inline TTo safe_cast(TFrom from)
{
return from;
}
Frank
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Ben Hutchings Guest
|
Posted: Wed Jan 14, 2004 11:03 pm Post subject: Re: Compatible types and ternary operator |
|
|
Frank Birbacher wrote:
| Quote: | Ben Hutchings wrote:
template
inline T std_convert(T value)
{
return value;
}
Note that the function doesn't really do any conversion.
Which could be changed:
template
inline TTo safe_cast(TFrom from)
{
return from;
}
|
If this is specialised with TTo = B* and TFrom = D*, where D inherits
privately from B, the conversion is inaccessible and so the program
is ill-formed. However, D's members and friends should be able to
convert D* to B*, which they will be able to if conversion is done
implicitly in the calling function. (I didn't spot this; credit is
due to someone whose article I can no longer find.)
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Mike Bland Guest
|
Posted: Thu Jan 15, 2004 10:33 am Post subject: Re: Compatible types and ternary operator |
|
|
On 2004-01-13, Dylan Nicholson <wizofaus (AT) hotmail (DOT) com> wrote:
| Quote: | A related question - is there a type of cast in C++ that is always
safe?
eg.
Base* x = foo() ? safe_cast<Base*>(new Dev1) : safe_cast<Base*>(new
Dev2);
I guess what I'm looking for is a cast that only compiles if an
implicit conversion or cast operator is available.
|
Isn't this pretty much what static_cast does? At least, if your idea
of "safety" means a safe cast for pointer types from derived to base
classes, or for types for which a conversion constructor or operator
is available?
Mike
-----= Posted via Newsfeeds.Com, Uncensored Usenet News =-----
http://www.newsfeeds.com - The #1 Newsgroup Service in the World!
-----== Over 100,000 Newsgroups - 19 Different Servers! =-----
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Matthias Hofmann Guest
|
Posted: Fri Jan 23, 2004 10:20 am Post subject: Re: Compatible types and ternary operator |
|
|
Ben Hutchings <do-not-spam-benh (AT) bwsint (DOT) com> schrieb in im Newsbeitrag:
[email]slrnc0aok4.vo.do-not-spam-benh (AT) tin (DOT) bwsint.com[/email]...
| Quote: | Frank Birbacher wrote:
Ben Hutchings wrote:
|
[snip]
| Quote: |
template
inline TTo safe_cast(TFrom from)
{
return from;
}
If this is specialised with TTo = B* and TFrom = D*, where D inherits
privately from B, the conversion is inaccessible and so the program
is ill-formed. However, D's members and friends should be able to
convert D* to B*, which they will be able to if conversion is done
implicitly in the calling function. (I didn't spot this; credit is
due to someone whose article I can no longer find.)
|
I do not quite understand what you mean by "implicitly in the calling
function" - did you mean to write "explicitly"? Or did you mean to write "in
the called function"? Otherwise, if D* can be converted to B* explicitly in
the calling function, why shouldn't it work within safe_cast()?
Best regards,
Matthias
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Ben Hutchings Guest
|
Posted: Fri Jan 23, 2004 9:09 pm Post subject: Re: Compatible types and ternary operator |
|
|
Matthias Hofmann wrote:
| Quote: | Ben Hutchings <do-not-spam-benh (AT) bwsint (DOT) com> schrieb in im Newsbeitrag:
[email]slrnc0aok4.vo.do-not-spam-benh (AT) tin (DOT) bwsint.com[/email]...
Frank Birbacher wrote:
Ben Hutchings wrote:
[snip]
template
inline TTo safe_cast(TFrom from)
{
return from;
}
If this is specialised with TTo = B* and TFrom = D*, where D inherits
privately from B, the conversion is inaccessible and so the program
is ill-formed. However, D's members and friends should be able to
convert D* to B*, which they will be able to if conversion is done
implicitly in the calling function. (I didn't spot this; credit is
due to someone whose article I can no longer find.)
I do not quite understand what you mean by "implicitly in the calling
function" - did you mean to write "explicitly"? Or did you mean to write "in
the called function"?
|
I do mean "implicitly". The conversion can be done as part of
parameter initialisation in the calling function (5.2.2/4).
| Quote: | Otherwise, if D* can be converted to B* explicitly in
the calling function, why shouldn't it work within safe_cast()?
|
Because D inherits privately from B and safe_cast isn't a member or
friend of D.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Matthias Hofmann Guest
|
Posted: Sat Jan 24, 2004 9:47 pm Post subject: Re: Compatible types and ternary operator |
|
|
Ben Hutchings <do-not-spam-benh (AT) bwsint (DOT) com> schrieb in im Newsbeitrag:
[email]slrnc1258i.104.do-not-spam-benh (AT) tin (DOT) bwsint.com[/email]...
| Quote: | Matthias Hofmann wrote:
Ben Hutchings <do-not-spam-benh (AT) bwsint (DOT) com> schrieb in im Newsbeitrag:
[email]slrnc0aok4.vo.do-not-spam-benh (AT) tin (DOT) bwsint.com[/email]...
Frank Birbacher wrote:
Ben Hutchings wrote:
[snip]
template
inline TTo safe_cast(TFrom from)
{
return from;
}
If this is specialised with TTo = B* and TFrom = D*, where D inherits
privately from B, the conversion is inaccessible and so the program
is ill-formed. However, D's members and friends should be able to
convert D* to B*, which they will be able to if conversion is done
implicitly in the calling function. (I didn't spot this; credit is
due to someone whose article I can no longer find.)
I do not quite understand what you mean by "implicitly in the calling
function" - did you mean to write "explicitly"? Or did you mean to write
"in
the called function"?
I do mean "implicitly". The conversion can be done as part of
parameter initialisation in the calling function (5.2.2/4).
|
I feel a little stoopid asking back again, but I am still confused - you
said that TFrom = D*, so how does parameter initialization cause any
conversion? Aren't you passing a D* anyway? Maybe a short code example would
help me understand... :-)
Best regards,
Matthias
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
John Potter Guest
|
Posted: Sun Jan 25, 2004 10:52 am Post subject: Re: Compatible types and ternary operator |
|
|
On 24 Jan 2004 16:47:00 -0500, "Matthias Hofmann"
<hofmann (AT) anvil-soft (DOT) com> wrote:
| Quote: | Maybe a short code example would help me understand.
|
template<typename TTo, typename TFrom>
inline TTo safe_cast(TFrom from) { return from; }
struct B { };
void f (B const*) { }
struct D : private B {
void g () const { f(this); }
void h () const { f(safe_cast<B*>(this)); }
};
void g (D const* d) { f(d); }
void h (D const* d) { f(safe_cast<B*>(d)); }
The implicit conversion in member g is the only valid call of f.
John
[ 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
|
|