8

What I mean is this, I have a function in c++ where I want to deposit money into an account. This function should be able to accept floats, doubles, integers, etc. as these are all valid forms of an input, as all I need is a number to deposit.

Thus, I declared:

template <typename type>
void Deposit(type t) {...}

Now the only issue I have is this: theoretically, a user of this class down the road could pass a char or string to this function and have unintended consequences of doing so. How would I go about restricting type to integers, floats, doubles and shorts? Is it possible to restrict this within the function definition so that others, when programming with this function get a compiler/linker error rather than having to use try{...} catch(...){...}?

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
rsdev
  • 140
  • 2
  • 4
  • 2
    What are you doing inside the function that you think will compile properly regardless of what type `t` is? – Sneftel Jun 30 '17 at 13:46
  • 3
    Please do not use floating point numbers for money. Makes it impossible to balance the books. Customers get uppity over the pennies – Ed Heal Jun 30 '17 at 13:46
  • 1
    You surely mean `typename` instead of `typedef`, right? – Rakete1111 Jun 30 '17 at 13:47
  • 1
    A `char` is a numerical type in C++ (and also what many compilers use for `typedef int8_t`) – UnholySheep Jun 30 '17 at 13:48
  • 1
    Note that char is a numeric type too. Also, do you have access to c++11? – iehrlich Jun 30 '17 at 13:48
  • @iehrlich I think the rule of thumb is: Respect the version language tags. If there are none, assume the newest. – Rakete1111 Jun 30 '17 at 13:49
  • @iehrlich I am using c++11. – rsdev Jun 30 '17 at 14:32
  • 1
    @EdHeal, as long as the decimals are kept to two places, it should be fine correct? Payroll requires two decimals, any form of transaction requires two decimals. I fail to see how leaving out floating point numbers in money would be useful, however I do not have a background in accounting... – rsdev Jun 30 '17 at 14:35
  • 2
    You do not need decimals. Use int as pennies. Then present that as appropriate. Avoiding floating point numbers remove the possibility of rounding errors creeping in – Ed Heal Jun 30 '17 at 14:39
  • 1
    Yes, my vote goes for storing a currency type for each transaction alongside the amount using the smallest possible granularity for that currency type as an `int` quantity. Normally people would use a database to do this :P but the same reasoning holds for C++ too. – underscore_d Jun 30 '17 at 14:43
  • 1
    you have XY problem here, you should use proper numeric type that works well with money (and can be converted from `int` `double` etcb and vise versa) rather than use template. – Slava Jun 30 '17 at 14:50
  • Please could you tell me how are you going to ensure that after every calculation you have just two decimal places on your double. – Ed Heal Jun 30 '17 at 15:10

2 Answers2

16

What you need std::is_arithmetic to constrain the template type to a arithmetic types (integral or floating point). You can use it like

template <typename T, typename std::enable_if<std::is_arithmetic<T>::value>::type* = nullptr>
void Deposit(T t) {...}
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • 6
    Note that `std::is_arithmetic` is true also for `bool`, that isn't one of the types listed in the question. – skypjack Jun 30 '17 at 15:14
  • This works for my (slightly different) case. I mostly understand how this works except for the "::type* = nullptr>" bit. Can somebody explain me that part? – MaestroMaus Mar 17 '19 at 12:30
  • 1
    @MaestroMaus `std::enable_if` has a member called `type` if the condition is true. What I am doing is saying that if the condition is true then the template has a non type template parameter of type `type*` (which is `void*` since the default type for `enable_if` is `void`) and it's value is `nullptr`. if the condition is false, the substitution fails, and the entire template is silently (no compiler error) discarded from the overload set. – NathanOliver Mar 17 '19 at 15:26
  • 1
    Documentation: https://en.cppreference.com/w/cpp/types/enable_if. Note that you can also use a `static_assert` to do essentially the same thing inside the class or function being defined. Here's the pattern to follow: [Use static_assert to check types passed to macro](https://stackoverflow.com/a/60769143/4561887). – Gabriel Staples Apr 25 '20 at 22:03
5

I am afraid you are getting wrong approach, you should create a class that properly work with money (including necessary operations for your domain - adding, subtracting etc), test it, add methods to print it and/or convert to string, and make your function accept only that type:

class Money {
    ...
};

void Deposit( Money amount );

So by adding constructors you can control which types can be accepted:

class Money {
public:
     explicit Money( double v );
     explicit Money( int64_t cents );
     Money( int64_t cents );
...
};

this way you can control what conversions can be done, and it would be done not only for this particular function but whole class. Otherwise you will need to re-implement the same logic in many functions (I doubt your system only would need functionality to deposit).

Johan
  • 74,508
  • 24
  • 191
  • 319
Slava
  • 43,454
  • 1
  • 47
  • 90
  • 1
    There is no need to store the dollars and cents separately. You can just say `__int64 cents`. Dollars are cents / 100. that's how must currency calculations work, using fixed point arithmetic. – Johan Sep 28 '18 at 10:36