PPP2 Chapter 19 Exercise 5 - Class Int

Exercise specifications:
Define a class Int having a single member of class int . Define construc-
tors, assignment, and operators + , – , * , / for it. Test it, and improve its
design as needed (e.g., define operators << and >> for convenient I/O).


I tried to create the class, but I'm having errors when using the constructor.

Here's 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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// Osman Zakir
// 10 / 25 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 19 Exercise 5
// Exercise Specifications:
/**
 * Define a class Int having a single member of class int . Define construc-
 * tors, assignment, and operators + , – , * , / for it. Test it, and improve its
 * design as needed (e.g., define operators << and >> for convenient I/O).
 */

#include <iostream>
#include <algorithm>
#include "../../cust_std_lib_facilities.h"

class Int
{
public:
	explicit Int(const int value = 0)
		: m_value{ value } { }
	Int(const int &value)
		: m_value{ value } { }
	Int(const int &&value)
	{
		std::move(value);
	}
	Int &operator+(const int value);
	Int &operator-(const int value);
	Int &operator*(const int value);
	Int &operator/(const int value);
	Int &operator=(const int value);
	Int &operator=(const int &value);
	Int &operator=(const int &&value);
	int getInt() const { return m_value; }
	void setInt(const int value) { m_value = value; }
private:
	int m_value;
};

std::ostream &operator<<(std::ostream &os, const Int &val);
std::istream &operator>>(std::istream &is, Int &val);

int main()
{
	Int myInt{ 5 };
}

Int &Int::operator+(const int value)
{
	int result = result = m_value + value;
	return *this;
}

Int &Int::operator-(const int value)
{
	int result = result = m_value - value;
	return *this;
}

Int &Int::operator*(const int value)
{
	int result = result = m_value * value;
	return *this;
}

Int &Int::operator/(const int value)
{
	int result = result = m_value / value;
	return *this;
}

Int &Int::operator=(const int value)
{
	m_value = value;
	return *this;
}

Int &Int::operator=(const int &value)
{
	m_value = value;
	return *this;
}

Int &Int::operator=(const int &&value)
{
	m_value = value;
	return *this;
}

std::ostream &operator<<(std::ostream &os, const Int &val)
{
	os << val;
	return os;
}

std::istream &operator>>(std::istream &is, Int &val)
{
	is >> val;
	return is;
}


I'm getting the error message "more than one instances of constructor "Int::Int" matches the argument list: Int::Int(const int value = 0), Int::Int(const int &value), Int::Int(const int &&value). Argument types are: int". I tried adding the keyword "explicit" in front the first one, since I'd prefer being able to create an Int object with just an int argument passed to the constructor. But I still get the same error. What else do I have to do to tell it which constructor to use?

Also, there are the input and output operators that are triggering the warning that all paths through them will call the functions themselves. Recursion. So what should I do on that?

Thanks in advance.

Edit: I changed the I/O operators to this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
std::ostream &operator<<(std::ostream &os, const Int &val)
{
	if (os)
	{
		os << val;
	}
	return os;
}

std::istream &operator>>(std::istream &is, Int &val)
{
	if (is)
	{
		is >> val;
	}
	return is;
}


But now, even though there are no warnings, I do get a stack overflow error when I try to run it.

The reason why I can compile it currently is because I've changed my main() function to this:
1
2
3
4
5
6
7
8
9
int main()
{
	Int myInt;
	myInt.setInt(5);
	std::cout << myInt << '\n';
	myInt = myInt + 4;
	std::cout << myInt << '\n';
	keep_window_open();
}


I'd really like to know how to tell it which of the other constructors to use when I pass in an int value, though.
Last edited on
1
2
3
4
5
6
7
8
std::ostream &operator<<(std::ostream &os, const Int &val)
{
	if (os)
	{
		os << val; // Call this same function again
	}
	return os;
}


That will just call itself forever and ever to infinity (or until the stack overflows).
So what would be the better (or best) way to define the << and >> operators, then?

And how do I get rid of the ambiguity error on calls to my constructors?
Last edited on
1
2
3
4
5
6
7
8
std::ostream &operator<<(std::ostream &os, const Int &val)
{
	if (os)
	{
		os << val.m_value;
	}
	return os;
}
Last edited on
48
49
50
51
52
Int &Int::operator+(const int value)
{
	int result = result = m_value + value;
	return *this;
}


This is all messed up. Line 49 has a redundant assignment to result. All you need is
int result = m_value + value;

And, you are adding value to the member m_value and storing it in a local variable which is ignored. You are trying to return Int&, so you actual return *this which is unmodified. You probably want to return Int (no reference) and use the line return Int(result); or some such.

You probably don't need both Int (const int) and Int(const int&). You can make do with just the first.

None of your operators take an Int as an argument, so you cannot do the following:
1
2
3
Int a(4);
Int b(4);
Int c = a + b;




Yeah, I messed up on the arithmetic operator definitions. My bad there. But for those functions not taking an Int object as an argument: do they need to? I mean, they already have the implicit this pointer since they're member functions of the class.

Int(const int) is a normal constructor and Int(const int&) is (meant to be) a copy constructor. And the Int(const int&&) is supposed to be a move constructor. I need to know the correct ways to declare and define them for this class if they're wrong.

@Repeater: The >> and << operators aren't member or friend functions, so I can't directly access m_value in them since it's a private member. That's what the getter member function is for.

Edit: Also, @doug4: Do I have to take out the reference operator from the assignment operators as well, or only from the arithmetic operators?

I'm thinking I should also define compound assignment operators for this class, by the way.

When I try to do:
1
2
3
4
5
6
7
8
std::istream &operator>>(std::istream &is, Int &val)
{
	if (is)
	{
		is >> val.getInt();
	}
	return is;
}


Even after defining a completely non-const version of getInt(), I still get the error message,
no operator ">>" matches these operands
operand types are: std::istream >> int
What's going on? std::istream >> int is supposed to be valid, isn't it? What did I do wrong? Should I make a friend of the class, so I can just m_value, after all?

Edit2: I made the >> operator a friend function. Now it's like this:
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// Osman Zakir
// 10 / 25 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 19 Exercise 5
// Exercise Specifications:
/**
 * Define a class Int having a single member of class int . Define construc-
 * tors, assignment, and operators + , – , * , / for it. Test it, and improve its
 * design as needed (e.g., define operators << and >> for convenient I/O).
 */

#include <iostream>
#include <algorithm>
#include "../../cust_std_lib_facilities.h"

class Int
{
public:
	Int(const int value = 0)
		: m_value{ value } { }
	Int operator+(const int value);
	Int operator-(const int value);
	Int operator*(const int value);
	Int operator/(const int value);
	Int &operator=(const int value);
	Int &operator=(const int &value);
	Int &operator=(const int &&value);
	int getInt() const { return m_value; }
	int getInt() { return m_value; }
	friend std::istream &operator>>(std::istream &is, Int &val);
private:
	int m_value;
};

std::ostream &operator<<(std::ostream &os, const Int &val);

int main()
{
	Int myInt{ 5 };
	std::cout << myInt << '\n';
	myInt = myInt + 4;
	std::cout << myInt << '\n';
	std::cout << "Please give me an int: ";
	Int myInt2;
	std::cin >> myInt2;
	std::cout << "You entered: " << myInt2 << '\n';
	keep_window_open();
}

Int Int::operator+(const int value)
{
	int result = m_value + value;
	return Int(result);
}

Int Int::operator-(const int value)
{
	int result = m_value - value;
	return Int(result);
}

Int Int::operator*(const int value)
{
	int result = m_value * value;
	return Int(result);
}

Int Int::operator/(const int value)
{
	int result = m_value / value;
	return Int(result);
}

Int &Int::operator=(const int value)
{
	m_value = value;
	return *this;
}

Int &Int::operator=(const int &value)
{
	m_value = value;
	return *this;
}

Int &Int::operator=(const int &&value)
{
	m_value = value;
	return *this;
}

std::ostream &operator<<(std::ostream &os, const Int &val)
{
	if (os)
	{
		os << val.getInt();
	}
	return os;
}

std::istream &operator>>(std::istream &is, Int &val)
{
	if (is)
	{
		is >> val.m_value;
	}
	return is;
}


And it compiles and runs correctly (or at least seems to, for now).
Last edited on
@Repeater: The >> and << operators aren't member or friend functions, so I can't directly access m_value in them since it's a private member. That's what the getter member function is for.


1
2
3
4
5
6
7
8
std::ostream &operator<<(std::ostream &os, const Int &val)
{
	if (os)
	{
		os << val.getInt();
	}
	return os;
}
I was able to get it to work. Code below:
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
// Osman Zakir
// 10 / 25 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 19 Exercise 5
// Exercise Specifications:
/**
 * Define a class Int having a single member of class int . Define construc-
 * tors, assignment, and operators + , – , * , / for it. Test it, and improve its
 * design as needed (e.g., define operators << and >> for convenient I/O).
 */

#include <iostream>
#include "../../cust_std_lib_facilities.h"

class Int
{
public:
	Int(const int value = 0)
		: m_value{ value } { }
	Int(const Int &val)
		: m_value{ val.m_value } { }
	Int(const Int &&val)
		: m_value{ std::move(val.m_value) } { }
	Int operator+(const int value);
	Int operator+(const Int &val);
	Int operator-(const int value);
	Int operator-(const Int &val);
	Int operator*(const int value);
	Int operator*(const Int &val);
	Int operator/(const int value);
	Int operator/(const Int &val);
	Int &operator=(const int value);
	Int &operator=(const Int &value);
	Int &operator=(const Int &&value);
	Int &operator+=(const int value);
	Int &operator+=(const Int &val);
	Int &operator-=(const int value);
	Int &operator-=(const Int &val);
	Int &operator*=(const int value);
	Int &operator*=(const Int &val);
	Int &operator/=(const int value); 
	Int &operator/=(const Int &val);
	int getInt() const { return m_value; }
	friend std::istream &operator>>(std::istream &is, Int &val);
private:
	int m_value;
};

std::ostream &operator<<(std::ostream &os, const Int &val);

int main()
{
	Int myInt{ 5 };
	std::cout << myInt << '\n';
	myInt += 4;
	std::cout << myInt << '\n';
	std::cout << "Please give me an int: ";
	Int myInt2;
	std::cin >> myInt2;
	std::cout << "You entered: " << myInt2 << '\n';
	Int myInt3 = myInt;
	std::cout << myInt3 << '\n';
	keep_window_open();
}

Int Int::operator+(const int value)
{
	int result = m_value + value;
	return Int(result);
}

Int Int::operator+(const Int &val)
{
	int result = m_value + val.m_value;
	return Int(result);
}

Int Int::operator-(const int value)
{
	int result = m_value - value;
	return Int(result);
}

Int Int::operator-(const Int &val)
{
	int result = m_value - val.m_value;
	return Int(result);
}

Int Int::operator*(const int value)
{
	int result = m_value * value;
	return Int(result);
}

Int Int::operator*(const Int &val)
{
	int result = m_value * val.m_value;
	return Int(result);
}

Int Int::operator/(const int value)
{
	int result = m_value / value;
	return Int(result);
}

Int Int::operator/(const Int &val)
{
	int result = m_value / val.m_value;
	return Int(result);
}

Int &Int::operator=(const int value)
{
	m_value = value;
	return *this;
}

Int &Int::operator=(const Int &value)
{
	m_value = value.m_value;
	return *this;
}

Int &Int::operator=(const Int &&value)
{
	m_value = value.m_value;
	return *this;
}

std::ostream &operator<<(std::ostream &os, const Int &val)
{
	if (os)
	{
		os << val.getInt();
	}
	return os;
}

Int &Int::operator+=(const int value)
{
	m_value += value;
	return *this;
}

Int &Int::operator+=(const Int &val)
{
	m_value += val.m_value;
	return *this;
}

Int &Int::operator*=(const int value)
{
	m_value *= value;
	return *this;
}

Int &Int::operator*=(const Int &val)
{
	m_value *= val.m_value;
	return *this;
}

Int &Int::operator-=(const int value)
{
	m_value -= value;
	return *this;
}

Int &Int::operator-=(const Int &val)
{
	m_value -= val.m_value;
	return *this;
}

Int &Int::operator/=(const int value)
{
	m_value /= value;
	return *this;
}

Int &Int::operator/=(const Int &val)
{
	m_value /= val.m_value;
	return *this;
}

std::istream &operator>>(std::istream &is, Int &val)
{
	if (is)
	{
		is >> val.m_value;
	}
	return is;
}


The code in main() worked even without the operators taking Ints as input, by the way. I added those in so that I could use an Int directly in those as operations as well.
Okay, for exercise 6 of the same chapter, I have to repeat the same exercise except for the class itself being Number<T> where T can be any numeric type. I copy-pasted the code from before to another file and made the necessary changes as I saw fit. I now have this:
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
// Osman Zakir
// 10 / 30 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 19 Exercise 6
// Exercise Specifications:
/**
 * Repeat the previous exercise, but with a class Number<T> where T can be
 * any numeric type. Try adding % to Number and see what happens when
 * you try to use % for Number<double> and Number<int> 
 */

#include <iostream>
#include "../../cust_std_lib_facilities.h"

template<typename T>
class Number
{
public:
	Number(const int value = 0)
		: m_value{ value } { }
	Number(const Number &val)
		: m_value{ val.m_value } { }
	Number(const Number &&val)
		: m_value{ std::move(val.m_value) } { }
	Number operator+(const T value);
	Number operator+(const Number &val);
	Number operator-(const T value);
	Number operator-(const Number &val);
	Number operator*(const T value);
	Number operator*(const Number &val);
	Number operator/(const T value);
	Number operator/(const Number &val);
	Number &operator=(const T value);
	Number &operator=(const Number &value);
	Number &operator=(const Number &&value);
	Number &operator+=(const T value);
	Number &operator+=(const Number &val);
	Number &operator-=(const T value);
	Number &operator-=(const Number &val);
	Number &operator*=(const T value);
	Number &operator*=(const Number &val);
	Number &operator/=(const T value);
	Number &operator/=(const Number &val);
	int getNumber() const { return m_value; }
	friend std::istream &operator>>(std::istream &is, Number<T> &val);
private:
	T m_value;
};

template<typename T>
std::ostream &operator<<(std::ostream &os, const Number<T> &val);

int main()
{
	Number<int> myNum{ 5 };
	std::cout << myNum << '\n';
	myNum += 4;
	std::cout << myNum << '\n';
	std::cout << "Please give me an int: ";
	Number<int> myNum2;
	std::cin >> myNum2;
	std::cout << "You entered: " << myNum2 << '\n';
	Number<int> myNum3 = myNum;
	std::cout << myNum3 << '\n';
	keep_window_open();
}

template<typename T>
Number<T> Number<T>::operator+(const T value)
{
	T result = m_value + value;
	return Number<T>(result);
}

template<typename T>
Number<T> Number<T>::operator+(const Number<T> &val)
{
	T result = m_value + val.m_value;
	return Number<T>(result);
}

template<typename T>
Number<T> Number<T>::operator-(const T value)
{
	T result = m_value - value;
	return Number<T>(result);
}

template<typename T>
Number<T> Number<T>::operator-(const Number<T> &val)
{
	T result = m_value - val.m_value;
	return Number<T>(result);
}

template<typename T>
Number<T> Number<T>::operator*(const T value)
{
	T result = m_value * value;
	return Number<T>(result);
}

template<typename T>
Number<T> Number<T>::operator*(const Number<T> &val)
{
	T result = m_value * val.m_value;
	return Number<T>(result);
}

template<typename T>
Number<T> Number<T>::operator/(const T value)
{
	T result = m_value / value;
	return Number<T>(result);
}

template<typename T>
Number<T> Number<T>::operator/(const Number<T> &val)
{
	T result = m_value / val.m_value;
	return Number<T>(result);
}

template<typename T>
Number<T> &Number<T>::operator=(const T value)
{
	m_value = value;
	return *this;
}

template<typename T>
Number<T> &Number<T>::operator=(const Number<T> &value)
{
	m_value = value.m_value;
	return *this;
}

template<typename T>
Number<T> &Number<T>::operator=(const Number<T> &&value)
{
	m_value = value.m_value;
	return *this;
}

template<typename T>
std::ostream &operator<<(std::ostream &os, const Number<T> &val)
{
	if (os)
	{
		os << val.getNumber();
	}
	return os;
}

template<typename T>
Number<T> &Number<T>::operator+=(const T value)
{
	m_value += value;
	return *this;
}

template<typename T>
Number<T> &Number<T>::operator+=(const Number<T> &val)
{
	m_value += val.m_value;
	return *this;
}

template<typename T>
Number<T> &Number<T>::operator*=(const T value)
{
	m_value *= value;
	return *this;
}

template<typename T>
Number<T> &Number<T>::operator*=(const Number<T> &val)
{
	m_value *= val.m_value;
	return *this;
}

template<typename T>
Number<T> &Number<T>::operator-=(const T value)
{
	m_value -= value;
	return *this;
}

template<typename T>
Number<T> &Number<T>::operator-=(const Number<T> &val)
{
	m_value -= val.m_value;
	return *this;
}

template<typename T>
Number<T> &Number<T>::operator/=(const T value)
{
	m_value /= value;
	return *this;
}

template<typename T>
Number<T> &Number<T>::operator/=(const Number<T> &val)
{
	m_value /= val.m_value;
	return *this;
}

template<typename T>
std::istream &operator>>(std::istream &is, Number<T> &val)
{
	if (is)
	{
		is >> val.m_value;
	}
	return is;
}


But I have a nasty linker error. It can't a matching input operator for the one I'm trying to call in main() on line 61. The exact linker error is as follows:
1>chapter19ex6.obj : error LNK2019: unresolved external symbol "class std::basic_istream<char,struct std::char_traits<char> > & __cdecl operator>>(class std::basic_istream<char,struct std::char_traits<char> > &,class Number<int> &)" (??5@YAAAV?$basic_istream@DU?$char_traits@D@std@@@std@@AAV01@AAV?$Number@H@@@Z) referenced in function _main
1>C:\Users\Osman\programming\visual_studio_2017\Projects\programming_principles_practice_using_c++\chapter19ex6\Debug\chapter19ex6.exe : fatal error LNK1120: 1 unresolved externals


What do I do to fix this?

I'll do the next part of the exercise after I've gotten rid of the linker error.
Last edited on
How are your files organised?

For a template class, the compiler needs to have access to the entire class definition, including method definitions within the translation unit where the template is instantiated. Unlike with normal classes, you can't just use the declarations, and then rely on the linker to link to the definitions.

Practically, this means that you have the definitions of the template class methods inside the header file, not in a separate source file.

(Yes, there are ways to work around this, but since you're learning, go with the simple, easy-to-understand option.)
> What do I do to fix this?

For the two ways in which this can be fixed, see: 'Why do I get linker errors when I use template friends?'
https://isocpp.org/wiki/faq/templates#template-friends
Topic archived. No new replies allowed.