Templates & Overloading

In short, I took a month break of programming and forgot a large chunk of what I knew.. So here I am trying to review it before Fall semester begins.

I'm currently reviewing templates and overloading. The goal of my program is to allow a user to do math with two numbers using either +, -, *, /, or %. Because % only works for int numbers, I looked up what I should do and found someone with the same problem:
https://stackoverflow.com/questions/1435253/c-syntax-question-if-var-type-int

The first reply to that stack overflow question answers my question - kind of - (it didn't work).
Specific Part I'm referring to:
https://gyazo.com/0e1895ceeb263da5b82d5c0cd2f753a5

My Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <iostream>

// function references
template <typename T> T calculate(T num1, char sym, T num2);
template <> int calculate(int num1, char sym, int num2); // Do I need the template <> part? The stack overflow post had it.

int main() {
	std::cout << "Note: Please make sure both numbers are the same data type (int, float, double).\n";
	
	std::cout << "Please input a number, symbol, and another number (i.e. 5 * 4): ";
	auto n1 = 0.0, n2 = 0.0;
	char symbol = ' ';
	std::cin >> n1 >> symbol >> n2;
	std::cout << "Answer: " << calculate(n1, symbol, n2) << '\n';
	
    return 0;   
}


// Will solve math problem user gives program no matter which data type the
// numbers are (int, float, double).
template <typename T>
T calculate(T num1, char sym, T num2) {
	if (sym == '*' || sym == 'x') {
		return num1 * num2;
	}
	else if (sym == '/') {
		return num1 / num2;
	}
	else if (sym == '+') {
		return num1 + num2;
	}
	else if (sym == '-') {
		return num1 - num2;
	}
	else {
		std::cout << "Error! symbol is not *, x, /, +, or -. Are you sure you input the correct symbol? \n";
		exit(1);
	}
}

// Overloads above function, so it will do ^ if double or float and will do the below if int.
template <>
int calculate(int num1, char sym, int num2) {
	if (sym == '*' || sym == 'x') {
		return num1 * num2;
	}
	else if (sym == '/') {
		return num1 / num2;
	}
	else if (sym == '+') {
		return num1 + num2;
	}
	else if (sym == '-') {
		return num1 - num2;
	}
	// The unique line:
	// '%' only works with ints.
	else if (sym == '%') {
		return num1 % num2;
	}
	else {
		std::cout << "Error! symbol is not *, x, /, +, -, or %. Are you sure you input the correct symbol? \n";
		exit(1);
	}
}

Side note for future reference (Note to self): A switch statement would've been better than the many if, else if statements.


Can someone please tell me what I'm doing wrong? When I try doing "5 % 5" for example it says:
Error! symbol is not *, x, /, +, or -. Are you sure you input the correct symbol?


Thanks!

Please Note: I may take 1-3 days to reply.
Last edited on
Do they really only work with ints?? I never knew that actually kinda scary simple thing like that..

You forgot the template argument:


calculate<int>(n1, symbol, n2)
Line 14 ^^^
Last edited on
highwayman wrote:
Do they really only work with ints?? I never knew that actually kinda scary simple thing like that..

You forgot the template argument:


calculate<int>(n1, symbol, n2)
Line 14 ^^^

Thanks for the quick reply! And yes, at least that's what the error told me earlier I believe when I tried doing % with decimal numbers.

As for the template argument <int>, that causes it to only show solid numbers as a result even if I do decimal numbers (from the first template function).
For example, typing: 20.4 * 32.55234 would show 664 rather than 664.067736.
True...

What If instead what we do is we make an if else statement:

1
2
3
4
5
6
if(symbol == '%'){
  std::cout << "Answer: " << calculate<int>(n1, symbol, n2) << '\n';
}
else{
  std::cout << "Answer: " << calculate<auto>(n1, symbol, n2) << '\n';
}
Last edited on
Given auto n1 = 0.0, n2 = 0.0, n1 and n2 have type double.

You'd need to examine what the user types in order to determine whether to treat that input as an int or double. You cannot use templates to do this: templates only make decisions at compile time.

See std::fmod and std::remainder for generic analogues of '%'.
Last edited on
Apologies for my late reply you two.

highwayman wrote:
True...

What If instead what we do is we make an if else statement:
1
2
3
4
5
6
if(symbol == '%'){
  std::cout << "Answer: " << calculate<int>(n1, symbol, n2) << '\n';
}
else{
  std::cout << "Answer: " << calculate<auto>(n1, symbol, n2) << '\n';
}

Smart. Thanks.

Although is the <int> and <auto> part actually a part of templates or just function overloading? I mean let's say I was using function overloading and no templates, could I do the <int> part still?
----------------------------
mbozzi wrote:

Given auto n1 = 0.0, n2 = 0.0, n1 and n2 have type double.

You'd need to examine what the user types in order to determine whether to treat that input as an int or double.

I was told I'm supposed to always initialize variables before I try to use them. And when I set them to 0, the answer would end up showing as an int even if I input doubles. Is there a proper way to initialize them before use without declaring their types before the user even inputs their data?

mbozzi wrote:
You cannot use templates to do this: templates only make decisions at compile time.

See std::fmod and std::remainder for generic analogues of '%'.

Ah ok, thanks I will check those out in a bit!

In response to the stuff about using auto as a template type argument (and forgetting template arguments), C++ is actually pretty good about determining arguments for templates, meaning in many cases you can leave them off and let the compiler sort it out. With that out of the way...

let's say I was using function overloading and no templates, could I do the <int> part still?

Nope.

I was told I'm supposed to always initialize variables before I try to use them.

That's good advice. For auto variables, that advice is in fact a rule.

Is there a proper way to initialize them before use without declaring their types before the user even inputs their data?

Nope, and that's by design. If you're looking for a way for a variable to be given values of different types (and you have access to C++17), might I suggest std::variant and get_if? https://en.cppreference.com/w/cpp/utility/variant/get_if#Example

Still, mbozzi's suggestion to use std::fmod is probably the best advice given what you're trying to do.

-Albatross
Is there a proper way to initialize them before use without declaring their types before the user even inputs their data?

No. Identifying what is and isn't possible is IMO the biggest conceptual challenge related to templates or other meta-programming tools.

The information you need in this case -- whether to call the int overload or the double overload -- depends on what the user types. You cannot know this before you execute your program. Because a template is a program that writes C++ code for you, it cannot make use of any information that isn't available when you're writing the code.

This is straightforward, but it can be difficult to grasp.

Figure out whether the user typed an int or double at runtime by examining their input; code both cases by hand.
For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
#include <iomanip>
#include <cstdlib>

bool parse_double(std::string const& s, double& d)
{
  char* parse_end = nullptr;
  d = std::strtod(s.data(), &parse_end);
  return (parse_end == s.data() + s.size()) && (s.size());
}

bool parse_int(std::string const& s, int& i)
{
  char* parse_end = nullptr;
  i = std::strtol(s.data(), &parse_end, 10);
  return (parse_end == s.data() + s.size()) && (s.size());
}

// N.B.: avoid specializing function templates.  
// http://www.gotw.ca/publications/mill17.htm
template <typename T>
void compute(T const&)
{ std::cout << "can't compute the modulus of non-int\n"; }

void compute(int i)
{ std::cout << i << " (mod 3) is equivalent to " << (i % 3) << '\n'; }

int main()
{
  for (std::string line; std::getline(std::cin, line);) 
  {
    if (int i; parse_int(line, i)) 
      compute(i);
    else if (double d; parse_double(line, d))
      compute(d);
    else
      std::cout << "discarding garbage: " << std::quoted(line) << '\n';
  }
}
Last edited on
Albatross: Thank you for your reply. That was very informative.

- - -

mbozzi wrote:
The information you need in this case -- whether to call the int overload or the double overload -- depends on what the user types. You cannot know this before you execute your program. Because a template is a program that writes C++ code for you, it cannot make use of any information that isn't available when you're writing the code.

This is straightforward, but it can be difficult to grasp.

Figure out whether the user typed an int or double at runtime by examining their input; code both cases by hand.
For example:
...

Ahh okay, thanks. I see what you mean. I'm often a bit very* slow at understanding so I appreciate the detail in your response!
Topic archived. No new replies allowed.