C++ casts

A

Alf P. Steinbach

* Old Wolf:
Here's another example:

double get() { return double(); }

The return statement creates a temporary double and then
returns it. No arguments here.

First point you're wrong, for the general case of _any_ type: there is no
requirement of a temporary in the standard. Quite the opposite. ;-) Second
point you're wrong, this code involves a 'double' rvalue, and there's no
such thing as a temporary non-class type rvalue. Third point you're wrong,
the notation with no arguments is not the cast notation described by
§5.2.3/1 and championed by Greg, which we were discussing, but the rvalue
creation notation described by §5.2.3/2, which is a very different beastie.

But you would say that the following does not create a
temporary double? :

double get() { return double(1); }

Yes, I would.
 
K

Kai-Uwe Bux

Jacob said:
Old said:
Huh?

Are you saying this is bad because someone might
remove the second statement at a later date?

Basically it is bad because it is wrong.

A "fraction" is a well defined concept and should
never be allowed to be outside the range [0.0, 1.0].

Hm, if that is a pending bug, then I suppose that wrinting

double fraction = 0.5;

is already pretty bad. Using the type double to represent a fraction that
*should never be allowed to be outside [0.0, 1.0]* is flawed then. You
should create a FractionClass (or template so that you can use float,
double, or higher precision) that will enforce the interval condition as an
invariant.


Best

Kai-Uwe Bux
 
J

Jacob

Old said:
Huh?

Are you saying this is bad because someone might
remove the second statement at a later date?

Basically it is bad because it is wrong.

A "fraction" is a well defined concept and should
never be allowed to be outside the range [0.0, 1.0].
 
G

Greg

Alf said:
* Old Wolf:

No, in general it's not, because it's equivalent to a C-style cast: the
standard uses the word equivalent.

But they are equivalent when converting simple types only, and not,
say, when converting class types. And simple type conversions carry
little risk - they are unlikely ever to crash. Pointer to type
conversions, on the other hand, can and do lead to crashes every now
and then.

Since pointer to type conversions carry the most risk, whichever
conversion notation makes them harder to express is the safer, better
notation. And since functional notation makes pointer to type
conversions much less convenient to express than the C cast notation -
functional notation is the better choice when performing an explicit
type conversion.

Greg
 
A

Alf P. Steinbach

* Greg:
But they are equivalent when converting simple types only, and not,
say, when converting class types.

Sorrry, that's incorrect -- but it's also irrelevant.

Since pointer to type conversions carry the most risk, whichever
conversion notation makes them harder to express is the safer, better
notation. And since functional notation makes pointer to type
conversions much less convenient to express than the C cast notation -
functional notation is the better choice when performing an explicit
type conversion.

I'd tend to agree with that if there were no other alternatives, choosing
between Baddum and Worsum.

However, we do have C++ named casts.
 
S

Stein Gulbrandsen

Jacob said:
This is certainly a way to compute the fraction n/total
without using casts, but it is conceptually wrong;
At the instance between the two statements "fraction"
will be incorrect and represents a pending bug.

Another way to write it without cast could be:

double fraction = 1.0 * n / total;

Not very nice, though.
 
A

Alf P. Steinbach

* Stein Gulbrandsen:
Another way to write it without cast could be:

double fraction = 1.0 * n / total;

Not very nice, though.

I think that's nearly nicest... It is correct and readable. An alternative
is to declare named 'double' versions of the arguments.
 
O

Old Wolf

Alf said:
* Old Wolf:

First point you're wrong, for the general case of _any_ type: there is no
requirement of a temporary in the standard. Quite the opposite. ;-) Second
point you're wrong, this code involves a 'double' rvalue, and there's no
such thing as a temporary non-class type rvalue. Third point you're wrong,
the notation with no arguments is not the cast notation described by
§5.2.3/1 and championed by Greg, which we were discussing, but the rvalue
creation notation described by §5.2.3/2, which is a very different beastie.

So it is the rvalue creation notation, but it does not create an
rvalue? I'm with you now...
Yes, I would.

#include <iostream>

int main()
{
double const &d = double(1);
double const &e = double(3);

std::cout << d << ' ' << e << std::endl;
}

(I presume this program does not have undefined behaviour).
The references d and e are undoubtedly bound to objects.
These objects aren't lvalues so they must be rvalues.

They aren't named so they must be temporary.

They had their lifetime extended by being bound to a const
reference, so this makes them a good candidate for being
temporary rvalues, wouldn't you say?
 
O

Old Wolf

Jacob said:
A "fraction" is a well defined concept and should
never be allowed to be outside the range [0.0, 1.0].

Not. (Non-negative) proper fractions are in the range [0, 1).
Negative proper fractions are in (-1, 0). Positive and negative
improper fractions are in [1, inf) and (-inf, -1] respectively.

All of these are fractions, of one sort or another.

NB. Naming proper, improper, and mixed fractions is
usually only done at an elementary level (apparently to
help learning), in practical mathematics the issue never comes up.
 
A

Alf P. Steinbach

* Old Wolf:

Please use a decent newsreader (not Gaggle) or fix the quoting manually.

So it is the rvalue creation notation, but it does not create an
rvalue? I'm with you now...

Not sure what you refer to by 'it', but you're contradicting yourself.


#include <iostream>

int main()
{
double const &d =3D double(1);
double const &e =3D double(3);

std::cout << d << ' ' << e << std::endl;
}

(I presume this program does not have undefined behaviour).

The references d and e are undoubtedly bound to objects.
Yes.


These objects aren't lvalues

Nope. They're lvalues, initialized from the rvalues. The standard goes on
and on about it.

so they must be rvalues.
Nope.


They aren't named so they must be temporary.

Yes.

They had their lifetime extended by being bound to a const
reference,
Yes.


so this makes them a good candidate for being
temporary rvalues, wouldn't you say?

Nope.
 
O

Old Wolf

Alf said:
* Old Wolf:

Please use a decent newsreader (not Gaggle) or fix the quoting manually.

Well, it looks just fine when reading via Gaggle, it is only when your
reply with the quoting appears that it looks funny, which would tend
to suggest that the problem is with your reader or a server somewhere
that reformats the messages as it goes.

Usually I reformat quotes to be less than 72 cols but sometimes I can't
be bothered. I will try harder in future :)
 
A

Alf P. Steinbach

* Alf P. Steinbach:
* Old Wolf:

First point you're wrong, for the general case of _any_ type: there is no
requirement of a temporary in the standard. Quite the opposite. ;-) Second
point you're wrong, this code involves a 'double' rvalue, and there's no
such thing as a temporary non-class type rvalue. Third point you're wrong,
the notation with no arguments is not the cast notation described by
§5.2.3/1 and championed by Greg, which we were discussing, but the rvalue
creation notation described by §5.2.3/2, which is a very different beastie.

I now think I was wrong & you were right on point two: it seems there are
temporary non-class rvalues, and in particular the result of a functional
notation or C style cast is probably a temporary object, this tidbit tucked
away in §3.10/6, phrased with qualifications so that it's difficult to grok.

In the example above this temporary, if any, can be elided.

Yes, I would.

Well, I wouldn't say that for sure any more. ;-)

However, regarding conclusions, the question was whether the functional
notation cast, which is equivalent to C-style cast, is a good idea/habit.

And, going up back up the thread, the proposed counter-example to the
position that it's not a good idea or habit, leading to the above, was

string name = string("john") + " doe";

And this is formally a cast, per the equivalence between functional notation
and C style cast notation, but, assuming 'string' is 'std::string', it's an
invocation of the 'std::string' constructor, not something that could turn
out to be or later become a const_cast or a reinterpret_cast, as T(x) can be
or become when T is a non-class type.

The fundamental difference is between class type and non-class type.


PS: Going down the thread instead of up, I discover to my horror that I
wrote about reference bound temporary objects "they're lvalues", in the
context of rvalue versus lvalue: the distinction rvalue/lvalue is only
meaningful for _expressions_, so it's only when the reference is _used_.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,021
Latest member
AkilahJaim

Latest Threads

Top