Robin
Robin

Reputation: 1698

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

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 useful ideas came up as comments to his answer). Again, thanks!

Upvotes: 19

Views: 7338

Answers (4)

Matthieu M.
Matthieu M.

Reputation: 299810

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.

Upvotes: 7

justin
justin

Reputation: 104698

you could create a small number holder type which had all the properties you wanted, then use that to interface with other types, such as Money.

Upvotes: 0

Kerrek SB
Kerrek SB

Reputation: 477040

How about a template plus compile-time trait check:

#include <type_traits>

// ...

template <typename T>
Money & operator*=(const T & n)
{
  static_assert(std::is_integral<T>::value, "Error: can only multiply money by integral amounts!");
  // ...
}

Upvotes: 15

Nicola Musatti
Nicola Musatti

Reputation: 18218

You could add a declaration for a private overload of your augmented assignment operator:

private:
  Money & operator*=( double i );

Upvotes: 8

Related Questions