c++boostboost-units

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!


Solution

  • 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