Reputation: 1698
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
Reputation: 299810
I can think of two ways to provide this:
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:
private
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
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
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
Reputation: 18218
You could add a declaration for a private overload of your augmented assignment operator:
private:
Money & operator*=( double i );
Upvotes: 8