Alex Rivera | Logout

How do I prevent an implicit cast double -> int?

Asked 2011-10-26T13:12:55.260
18

Question as above, more details below:

I have a class Money to deal with... well, you guessed what. I am very strict about not allowing Money and double to interact(*), so the following code is not possible:

Money m1( 4.50 );
double d = 1.5;
Money m2 = m1 * d; // <-- compiler error

Now I'm thinking about allowing multiplication of Money with int, as in "you have 6 pieces of cake for $4.50 each (so go and find cheaper cake somewhere)."

class Money
{
    Money();
    Money( const Money & other );
    explicit Money( double d );
    ...
    Money & operator*=( int i );
    ...
}
inline const Money operator*( const Money & m, int i ) { return Money( m ) *= i; }
inline const Money operator*( int i, const Money & m ) { return Money( m ) *= i; }

That works fine, but... unfortunately, C++ does implicit casts from double to int, so suddenly my first code snippet will compile. I don't want that. Is there any way to prevent implicit casts in this situation?

Thanks! -- Robin

(*) Reason: I have lot's of legacy code that handles all Money-related stuff with double, and I don't want those types confused until everything run with Money.

Edit: Added constructors for Money.

Edit: Thanks, everyone, for your answers. Almost all of them were great and helpful. R. Martinho Fernandes' comment "you can do inline const Money operator*( const Money & m, double d ) = delete;" was actually the answer (as soon as I switch to a C++11-supporting compiler). Kerrek SB gave a good none-C++11 alternative, but what I ended up with using is actually Nicola Musatti's "overload long" approach. That's why I'm flagging his answer as "the answer" (also because all the use

Edit
Report

1 Answer

7

I can think of two ways to provide this:

  • either using template and "concept" checks
  • or using "forbidden" overloads

Using overloads is the C++11 solution. C++11 introduces the delete keyword especially for your case!

Money& operator*=(int i);
Money& operator*=(float f) = delete;

Money operator*(Money m, int i) { return m*i; }
Money operator*(Money m, float f) = delete;

Money operator*(int i, Money m) { return m*i; }
Money operator*(float f, Money m) = delete;

The old way (C++03) to go about that was double:

  • in the class, declare the method private
  • don't define the method and wait for the linker to complain

The second is a safeguard in the class method case, and the only way in the free-method case. It's sad that it's only detected at link-time... and the delete keyword is just much nicer ;)


Using template is another solution. You may use either std::enable_if or static_assert: one will remove the function from the overload set (SFINAE) while the other will make instantiation fail (compiler error).

Example:

// For enable_if
template <typename T>
std::enable_if<std::is_integral<T>::value, Money&> operator*=(T t);

template <typename T>
std::enable_if<std::is_integral<T>::value, Money> operator*(Money m, T t);

template <typename T>
std::enable_if<std::is_integral<T>::value, Money> operator*(T t, Money m);

The examples for static_assert are more natural (it just like a regular assert, really).


I would rather recommend the overload + delete if you have it. If you don't then a fallback to the template case is probably the best solution, because it's easier to correct compiler errors than linker ones.

answered 2011-10-26T13:55:32.370

Your Answer