valid use of cast

C

conrad

I have the following:
1: template< class Sumtype, class Averagetype >
2: Averagetype Average( Sumtype* p_array, Averagetype p_count )
3: {
4: int index;
5: Sumtype sum = 0;
6: for( index = 0; p_count > index; index++ )
7: sum += p_array[index];
8: return (Averagetype)sum / p_count;
9: }

It's from a book that I am currently reading.
I'm curious to know what the standard says
about the cast to Averagetype?
 
V

Victor Bazarov

conrad said:
I have the following:
1: template< class Sumtype, class Averagetype >
2: Averagetype Average( Sumtype* p_array, Averagetype p_count )
3: {
4: int index;
5: Sumtype sum = 0;
6: for( index = 0; p_count > index; index++ )
7: sum += p_array[index];
8: return (Averagetype)sum / p_count;
9: }

It's from a book that I am currently reading.
I'm curious to know what the standard says
about the cast to Averagetype?

Nothing. There is no mention of 'Averagetype' in the Standard.
On the serious note, what do you expect the Standard to say?
C-style casts are part of the language, they usually end up
being static_cast or const_cast or reinterpret_cast or some
kind of combination of both, or, sometimes, something else
that none of standard C++ casts support. In your case the
cast is probably static_cast.

V
 
P

Phlip

conrad said:
1: template< class Sumtype, class Averagetype >
2: Averagetype Average( Sumtype* p_array, Averagetype p_count )
3: { ....
8: return (Averagetype)sum / p_count;
9: }

It's from a book that I am currently reading.
I'm curious to know what the standard says
about the cast to Averagetype?

It calls Averagetype::Averagetype(Sumtype const & x), which must exist (to
within input type variations).

Next, it could have been written with constructor notation:

Averagetype(sum)

Or as an elaborate_cast:

static_cast<Averagetype>(sum)

Question: Which is better style?
 
J

James Kanze

It calls Averagetype::Averagetype(Sumtype const & x), which
must exist (to within input type variations).

Alternatively, it might call Sumtype::eek:perator Averagetype() (or
Averagetype::Averagetype( Sumtype& x ), since the conversion is
applied to an lvalue.
Next, it could have been written with constructor notation:

Or as an elaborate_cast:

Question: Which is better style?

None of the above:).

The original code contains somewhat of a mishmash of types; for
all intents and purposes, Averagetype will almost always be an
integral type (since it will be type deduced from the number of
elements in the array, and most people won't use something like
42.0 to specify the number of elements). All the conversion
does is (possibly) reduce precision, if e.g. Sumtype is double,
and Averagetype is size_t. (But don't you find it curious to
return the average of an array of floating point as a size_t to
begin with?)

FWIW, of course, the idiomatic way of calculating the average in
C++ would be something like:

// For v of type V...
std::accumulate( v.begin(), v.end(), V::value_type() ) /
v.size() ;

Except, of course, if V::value_type is integral, you might
prefer 0.0, to avoid overflow and provide more precision in the
results.

And of course, the basic algorithm is only valid for very small
data sets. Naively adding the values in an array can quick
result in errors if the number of elements is large and/or the
the elements are of different magnitudes.
 
B

BobR

Phlip said:
It calls Averagetype::Averagetype(Sumtype const & x), which must exist (to
within input type variations).

Next, it could have been written with constructor notation:

Averagetype(sum)

Or as an elaborate_cast:

static_cast<Averagetype>(sum)

Question: Which is better style?

The C++ cast. It is so ugly, you can't ignore it!
Better error/warnings when compiling.

Also, it makes it easy to search for with an editor.
( search (non-whole word): "_cast<", will find 'em all. Try that with a
C-style cast! )

I use the 'constructor-notation' for simple things I know are (relatively)
safe:

for( int i(0); size_t( i ) < somevector.size(); ++i ){
// some op that needs an int.
}
.... the 'size_t( i )' shuts-up the compiler warning.
 
C

Chris ( Val )

Phlip <[email protected]> wrote in message...

[snip and agree]
for( int i(0); size_t( i ) < somevector.size(); ++i ){
// some op that needs an int.
}
... the 'size_t( i )' shuts-up the compiler warning.

Hi Bob,

I am interested to know why you use this version:

for( int i(0); size_t( i ) < somevector.size(); ++i )

....as opposed to this one:

for( size_t i(0); i < somevector.size(); ++i )
?

Isn't your version casting an int to a size_t every time
it the expression is evaluated?

Where as (please correct me if I'm wrong gang), I don't
think the latter version does?

Cheers,
Chris Val
 
V

Victor Bazarov

Chris said:
Phlip <[email protected]> wrote in message...

[snip and agree]
for( int i(0); size_t( i ) < somevector.size(); ++i ){
// some op that needs an int.
}
... the 'size_t( i )' shuts-up the compiler warning.

Hi Bob,

I am interested to know why you use this version:

for( int i(0); size_t( i ) < somevector.size(); ++i )

...as opposed to this one:

for( size_t i(0); i < somevector.size(); ++i )
?

Isn't your version casting an int to a size_t every time
it the expression is evaluated?

Where as (please correct me if I'm wrong gang), I don't
think the latter version does?

It can be because inside the loop body 'i' is used all over the
place where an 'int' is expected (in expressions where signed
values are used, in function arguments that need 'int', etc.)
Having a 'size_t' in those places is dangerous since it can
cause other parts of the expression to be made unsigned with
results you might not want.

This is just a speculation of course.

V
 
B

BobR

Chris ( Val ) wrote in message...
[snip and agree]
for( int i(0); size_t( i ) < somevector.size(); ++i ){
// some op that needs an int.
}
... the 'size_t( i )' shuts-up the compiler warning.

Hi Bob,

I am interested to know why you use this version:

for( int i(0); size_t( i ) < somevector.size(); ++i )

...as opposed to this one:

for( size_t i(0); i < somevector.size(); ++i )
?

Isn't your version casting an int to a size_t every time
it the expression is evaluated?

Hi Chris,

Pretty much what Victor said.
My current project would require me to cast hundreds of places if I used the
'for( size_t ...)' version. So, I'd rather cast in one single place, until
I'm ready to tackle the final 23 functions [converting an old 'C' set of
programs into one 'C++' (monster!) program. Creates a 3D object out of a few
random numbers.].

Going from 'int' to 'size_t' is pretty safe (unless a really big number will
cause failure.<G>).
Use the proper type where you can, pick the lesser of two evils in other
places.

When I have a choice, I use the 'size_t', especially when dealing with an
index.
My project did use an negative index, and it took me forever to 'clean'.

struct Thing{ // the 'C' stuff I 'inherited'.
double peak;
double array[6];
};
Thing thing;
for( int i( -1 ); i < 6; ++i ){ // size_t( i) == problem here
thing.array[ i ] = someassign;
// first loop modifies 'peak'!! I HATE that.
}

Drove me nuts for a while! (made 'array' a std::vector, and kept
segfaulting.).
( yeah, I'm still nuts, but not for that construct! <G>).

My project works smooth as glass now, so I'm working on the interfaces
(separating wxWidgets from the 'workhorse', then I'll optimise the
bottlenecks (and should be able to eliminate (most of) the for loops).)

Ooops, running off-topic again. Sorry.
Where as (please correct me if I'm wrong gang), I don't
think the latter version does?

Not AFAIK.
Depends on the requirements/restrictions in the loop body.
[ How do you know the compiler does not optimise it? ]
 
C

Chris ( Val )

Chris ( Val ) wrote:




[snip and agree]
for( int i(0); size_t( i ) < somevector.size(); ++i ){
// some op that needs an int.
}
... the 'size_t( i )' shuts-up the compiler warning.
I am interested to know why you use this version:
for( int i(0); size_t( i ) < somevector.size(); ++i )
...as opposed to this one:
for( size_t i(0); i < somevector.size(); ++i )
?
Isn't your version casting an int to a size_t every time
it the expression is evaluated?
Where as (please correct me if I'm wrong gang), I don't
think the latter version does?

It can be because inside the loop body 'i' is used all over the
place where an 'int' is expected (in expressions where signed
values are used, in function arguments that need 'int', etc.)
Having a 'size_t' in those places is dangerous since it can
cause other parts of the expression to be made unsigned with
results you might not want.

This is just a speculation of course.

Hi Victor,

Yes. I agree, and thank you for the feedback.

The initial reason for my concern was that of efficiency,
given that the condition is cast through each iteration
of the loop.

Ideally, it is recommended that "std::vector<int>::size_type"
be used, even though it may be a size_t internally.

Cheers,
Chris Val
 
C

Chris ( Val )

BobR wrote in message...



Which (size_t, int) you use depends on .....

Hi Bob,

I'm not quite sure what you are getting at here?

Are you saying it is possible that the second example
I provided is or can be cast through each iteration?

I do not believe that to be the case in that context.

Cheers,
Chris Val
 
C

Chris ( Val )

Chris ( Val ) wrote in message...

[snip]

Hi Bob,
Going from 'int' to 'size_t' is pretty safe (unless a really big number will
cause failure.<G>).

But an integer as small as '-1' can cause you a lot of grief :)
Use the proper type where you can, pick the lesser of two evils in other
places.

I agree, and the main reason for my post :)

If std::vector::size() returns an unsigned type, then
by your own laws of evil you should have used:

for( size_t i(0); i < ....; ... )
{
// ...
When I have a choice, I use the 'size_t', especially when dealing with an
index.
My project did use an negative index, and it took me forever to 'clean'.
Understandable.

struct Thing{ // the 'C' stuff I 'inherited'.
double peak;
double array[6];
};
Thing thing;
for( int i( -1 ); i < 6; ++i ){ // size_t( i) == problem here
thing.array[ i ] = someassign;
// first loop modifies 'peak'!! I HATE that.
}

[snip]

Yes, as noted above, this is where a negative integer can
really cause you a lot of grief with an unsigned type such
as size_t.

Cheers,
Chris Val
 
B

BobR

Chris ( Val ) wrote in message...
Hi Bob,

I'm not quite sure what you are getting at here?

I think I was, more or less, agreeing with you. <G>

Would you rather cast in one place, or one hundred places?
[ assume that in the future you might fix the problem, and need to remove
the cast(s). ]
Are you saying it is possible that the second example
I provided is or can be cast through each iteration?

I imagine it *might* be possible. ( I wouldn't expect the standard commitee
to state that a container size_type shall be a 32 bit unsigned type. Too
restrictive.).

I use GCC(MinGW on the windows partition), and std::vector::size_type is a
typedef of size_t, so, I just use size_t. I figure there is no cast doing it
that way since MyVector.size() just returns a size_t. [newbies: stick to
'size_type' until you know enough to make a decision about it. It's the
correct way.].
I do not believe that to be the case in that context.

Keep in mind that what started this part of the discussion was me showing a
way to avoid static_cast<size_t>(intnum) in some places (an int and a size_t
are the same size (4) on my machine, it's only the interpretation of the MSB
that makes the difference.).
It's good for a newbie to understand what happens when you do:

int size( -1 );
size_t Vsize( size ); // ignore warning, (if you get one)
// std::vector<int> MyVec( Vsize );
// newbie: is it very small, or very big?

I remember trying (assume std::):
vector<vector<int> > Vv( Vsize, Vsize );
.... which runs out of memory on my machine (duh!).
[ anyone have a google-byte RAM machine handy? ]

[ corrections welcome ]
 
C

Chris ( Val )

Chris ( Val ) wrote in message...

Hi Bob,
I think I was, more or less, agreeing with you. <G>

Oh, ok :)
Would you rathercastin one place, or one hundred places?

Of course, one place, but only if it was absolutely required.

In the case you presented, I don't see that there was a reason
to cast at all, had you chosen the correct data type to work
with from from the start: 'std::size_t' or preferably
'std::vector said:
[ assume that in the future you might fix the problem, and
need to remove thecast(s). ]
Are you saying it is possible that the second example
I provided is or can becastthrough each iteration?

I imagine it *might* be possible.

Unless it is somehow explicitly modified in the body of
the for loop, once it is defined as the initialiser value,
I don't think it is re-evaluated again.

( I wouldn't expect the standard commitee
to state that a container size_type shall be a 32 bit
unsigned type. Too restrictive.).

The only constrain that the standard places on it is that
it returns an "unsigned integral type", that can represent
the size of the largest object in the allocation model.

So, knowing the return type of the vector iterator means
that there was no need for casting in the frist place.

for( std::size_t Index(0); ...; ... )
for( vector<T>::size_type Index(0); ...; ... )

I've defined the local valiable 'Index' to the
correct type from the start. This means that no
additional evaluation of the conditional expression
is taking place.
IuseGCC(MinGW on the windows partition), and std::vector::size_type is a
typedef of size_t, so, I justusesize_t. I figure there is nocastdoing it
that way since MyVector.size() just returns a size_t. [newbies: stick to
'size_type' until you know enough to make a decision about it. It's the
correct way.].

Yes, 'size_type' is officially promoted to be the prefered construct.
Keep in mind that what started this part of the discussion was me showing a
way to avoid static_cast<size_t>(intnum) in some places (an int and a size_t
are the same size (4) on my machine, it's only the interpretation of the MSB
that makes the difference.).

[snip]

Yes, but I'm still not sure if you understand the part I was homing in
on :)
I wasn't worried so much about the fact that you used 'size_t'... It
was the
position as used in the for loop that I was questioning for efficiency
reasons.

Cheers,
Chris Val
 
A

Andrew Koenig

8: return (Averagetype)sum / p_count;
I'm curious to know what the standard says
about the cast to Averagetype?

I haven't read all the comments in this thread, but so far none of them has
noted the following:

Because a cast is essentially a unary operator, it binds more tightly than
division. Therefore,

return (Averagetype)sum / p_count;

is interpreted as

return ((Averagetype) sum) / p_count;

This characteristic is particularly important if sum has an integral type
and Averagetype has a floating-point type. In that case, the cast converts
sum to floating-point and only then divides it by p_count. This conversion,
in turn, is important because otherwise the result of the division would be
truncated to an integer.

Because this precedence subtlety is so important, and so easily missed, I
would be inclined to write the second return statement above instead of the
first.
 

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

Forum statistics

Threads
474,200
Messages
2,571,046
Members
47,646
Latest member
xayaci5906

Latest Threads

Top