Boost logo

Boost Users :

From: Fernando Cacciola (fernando_cacciola_at_[hidden])
Date: 2005-05-27 13:02:11


"Toon Knapen" <toon.knapen_at_[hidden]> escribió en el mensaje
news:4295AA07.5030102_at_fft.be...
> Following small program does not give the expected result. It prints some
> big negative value if compiled with my gcc 3.4.3 on linux or intel-linux
> 8.1.
>
> However commenting out either (one suffices) of the commented out lines
> makes it work correctly. I don't understand what is happening here, can
> anybody what is wrong with this function 'convert' or is the use of the
> conversion library wrong?
>
> <code>
> #include <boost/numeric/conversion/cast.hpp>
> #include <iostream>
>
> template<typename Target, typename Source>
> inline
> typename ::boost::numeric::converter<Target,Source>::result_type
> //convert ( const Source& arg ) // using this signatures makes it work
> convert ( Source arg )
> { return ::boost::numeric::converter< Target, Source >::convert( arg ) ; }
> // { Target temp = ::boost::numeric::converter< Target, Source
> >::convert( arg ) ; return temp ; }
>
> int main() {
> int big = 900 ;
> ptrdiff_t diff = 16 ;
> std::cout << std::min( big, convert< int >( diff ) ) << std::endl ;
>
> return 0 ;
> }
> </code>

OK, this is a good one :-)

Whenever Source==Target, the library attempts to reduce the conversion to a
no-op;
That is, for the following code:

numeric::converter<int,int>::convert(123);

the instantiated convert function is this:

int const& convert( int const& s ) { return s ; }

Notice that it takes a reference and returns a reference.

As such,

numeric::converter<int,int>::result_type is "int const&"; that is, a
reference.

and:

numeric::converter<int,int>::argument_type is also "int const&"

Now... you've wrapped that function into your own, which after all the
template instantiations is equivalent to:

int const& your_convert( int s )
{
  return s ;
}

Now, combine this with std::min() (or any other template using argument-type
deduction) and the argument
becomes "int const&" which explains your unexpected results.

A "fix" on your side would be to use
::boost::numeric::converter<Target,Source>::argument_type;
but this isn't practical because users are forced to specify "Source"
explicitely.

So you need "Source" to be the source type, but then, you also _need_
"Target" to be the result type:

template<typename Target, typename Source>
inline Target convert2 ( Source arg )
{
  return ::boost::numeric::converter< Target, Source >::convert( arg ) ;
}

Incidentally, boost::numeric_cast<> (the new one) is equivalent to your code
so is just as equally buggy.

I'm afraid I have to patch it not to use result_type anymore.

P.S: One could thing that the traits class, in the case of a trivial
conversion, could define bare types instead of references (that is, making
result_type being just "T" instead of "T const&). Altough this OK for
builtin types, it isn't for the UDT, and it is precisly in the case of UDTs
were the "reduction to a no-op in the trivial case" becomes really
important. This means though that numeric_cast<> is better not used with
UDTs in generic context when T==S is possible (because the optimization is
then lost)

P.S.2: Of course the alternative is to use "Source const&" instead of
"Source"... I don't really remember the details but I seem to recall that
the original numeric_cast<> used a non-reference argument for some good
reason; and so I just kept that.

Thanks for the report!

Fernando Cacciola
SciSoft


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net