sinad sinad - 11 days ago 5
C++ Question

multiplying a scalar by a boost.units.quantity (automatic type conversion issues?)

This is a really minimalist example. I'm using Boost.Units in the following way:

#define REAL double
...
using namespace boost::units;
quantity<si::velocity, REAL> v;


then sometimes I need to have something like

quantity<si::velocity, REAL> v_halved;
v_halved = 0.5 * v;


This works ok, because the compiler treats
0.5
as
double
. But, when
REAL
is something different then I get a compile error, e.g. if I changed the definition of
REAL
to
long double
the compile complains:

error: no match for ‘operator*’ in ‘5.0e-1 * v’
/usr/include/boost/units/detail/one.hpp:58: note: candidates are: boost::units::one boost::units::operator*(const boost::units::one&, const boost::units::one&)


Looking into the Boost.Units documentation I founded that the
operator*
is overloaded as follows:

// runtime scalar times quantity
template<typename Unit, typename X>
multiply_typeof_helper< X, quantity< Unit, X > >::type
operator*(const X & lhs, const quantity< Unit, X > & rhs);


Although it is clear from the definition that the scalar and the internal type of the quantity must be the same, I would expect that the compiler automatically converts the type when the conversion can be done implicitly (like from
double
to
long double
). However I think that I might be missing something because the automatic type conversion certainly works for other simple functions like
long double f(long double const & ld)
.

My problem is that I have used expressions like
v_halved = 0.5 * v
quite a lot and my project has become considerably large already, and it is only now, after having to define
REAL
as
long double
that I realize this is a problem. So, I was wondering about a workaround/solution to this, I'm aware that
static_cast<REAL>(0.5)
would be a solution, but I still feel that I'm missing something about the compiler not being able to automatically convert the scalar to the right type.

Thanks a lot in advance!

Answer

template functions are different that non template function. Compiler decide to select template function without considering the implicit casting/promotions. It just look for exact matching.

This is how C++ works in this area.

To get exact matching you would need such operator * definition (notice this extra template param Y):

template<typename Unit, typename X, typename Y> 
  typename multiply_typeof_helper< X, quantity< Unit, X > >::type 
  inline operator*(const Y & lhs, const quantity< Unit, X > & rhs)
{
    return static_cast<X>(lhs) * rhs;
}

but I am afraid that will interfere with boost definition of *. You can play with this - like define your own quantity which will derive almost entirely from boost - but define multiplication in different way, proposed by me.

Just decide what is easier for you to go with

  • do static cast (or better just add L - 0.5L to make long double constant)
  • or do some combinations with templates.
Comments