Skip to content

Commit cd54393

Browse files
committed
Remove <=> and hidden friends from operators exercise.
This exercise is used both in the essentials and the advanced course. We can therefore not require students to work with hidden friends and <=>. Here, all mentions of <=> are removed (there is a dedicated exercise on the third day of the advanced course), and the tasks are rephrased such that students can work on this exercise also in the essentials course. The diff between the original file and the solution is minimised, so viewing the solution with a diff program hopefully helps the students.
1 parent 375bbd4 commit cd54393

File tree

3 files changed

+40
-40
lines changed

3 files changed

+40
-40
lines changed

exercises/operators/README.md

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,23 @@
11

22
## Instructions
33

4-
STEP 1
5-
- Add a free operator<<, reusing str(), and simplify main() first lines.
6-
- Replace equal() with operator==(), and upgrade tests.
7-
- Add operator!=(), reusing operator==(), and upgrade tests.
8-
- Replace compare() with operator<=>(), reusing <=> between doubles,
9-
and upgrade tests.
10-
- Replace multiply() with operator*(), and upgrade tests.
4+
### Main Tasks
5+
- Write an ostream operator with the following signature: `operator<<(ostream &, Fraction const &)`.
6+
The `str()` function of Fraction will help you to implement it. Use this operator to make the `cout`s in the first lines of `main()` look a bit more natural.
7+
- **Note**: If you do this exercise as part of the advanced course, implement the operators as hidden friends.
8+
- Replace the function `equal()` with `operator==()`, and upgrade tests.
9+
Note that equality isn't the same as equivalence. The compare function returns 0
10+
for 1/1, 2/2, etc, but these are not equal.
11+
- Add `operator!=()`, reusing `operator==()`, and upgrade tests.
12+
- Replace `multiply()` with `operator*()`, and upgrade tests.
1113

12-
STEP 2
13-
- Replace TestResultPrinter::process() with operator()(), and upgrade CHECK().
14-
15-
OPTIONAL STEP 3
16-
- Add an inplace multiplication operator*=(), and add tests.
17-
- Review operator*() so to reuse operator*=().
18-
- Ensure calls to operator*=() can be chained, the same as operator<<().
14+
### Optional if you have time
15+
- Add an inplace multiplication `operator*=()`, and add tests.
16+
- Review `operator*()` so to reuse `operator*=()`.
17+
- Ensure calls to `operator*=()` can be chained, the same as `operator<<()`.
1918

2019
## Take away
2120

21+
- Operators can make certain expressions much more readable.
2222
- Do not confuse equality and equivalence.
2323
- We can very often implement an arithmetic operator@ in terms of operator@=.
24-
- When implementing <=>, you get <, >, <=, >= for free.
25-
- Object-functions are very used with standard algorithms,
26-
yet tend to be often replaced by lambdas in modern C++.

exercises/operators/operators.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ class Fraction {
1919
return (lhs.m_num==rhs.m_num) && (lhs.m_denom==rhs.m_denom);
2020
}
2121

22+
// This function compares two fractions, and returns
23+
// -1 if lhs < rhs
24+
// 0 if they denote the same value (equivalence)
25+
// 1 if lhs > rhs
2226
friend int compare( Fraction const & lhs, Fraction const & rhs ) {
2327
int v1 = lhs.m_num * rhs.m_denom;
2428
int v2 = rhs.m_num * lhs.m_denom;
@@ -86,6 +90,7 @@ int main() {
8690

8791
// equivalence & comparison
8892
std::cout<<std::endl;
93+
CHECK(!equal(third,Fraction{2,6}));
8994
CHECK(compare(third,Fraction{2,6})==0);
9095
CHECK(compare(third,Fraction{1,4})>0);
9196
CHECK(compare(third,Fraction{2,4})<0);
@@ -98,6 +103,7 @@ int main() {
98103
CHECK(compare(multiply(3,third),Fraction{1,1})==0);
99104
CHECK(equal(multiply(3,third).normalized(),1));
100105

106+
101107
// end
102108
std::cout<<std::endl;
103109

exercises/operators/solution/operators_sol.cpp

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#include <iomanip>
22
#include <iostream>
33
#include <sstream>
4-
#include <compare>
54
#include <numeric>
65

76
class Fraction {
@@ -24,8 +23,16 @@ class Fraction {
2423
return !(lhs==rhs);
2524
}
2625

27-
friend auto operator<=>( Fraction const & lhs, Fraction const & rhs ) {
28-
return ((lhs.m_num*rhs.m_denom)<=>(rhs.m_num*lhs.m_denom));
26+
// This function compares two fractions, and returns
27+
// -1 if lhs < rhs
28+
// 0 if they denote the same value (equivalence)
29+
// 1 if lhs > rhs
30+
friend int compare( Fraction const & lhs, Fraction const & rhs ) {
31+
int v1 = lhs.m_num * rhs.m_denom;
32+
int v2 = rhs.m_num * lhs.m_denom;
33+
if (v1 < v2) return -1;
34+
else if (v1 > v2) return 1;
35+
else return 0;
2936
}
3037

3138
Fraction & operator*=(Fraction const & other) {
@@ -59,7 +66,7 @@ class TestResultPrinter {
5966

6067
TestResultPrinter( unsigned int a_width ) : m_width(a_width) {}
6168

62-
void operator()(std::string const & what, bool passed) {
69+
void process(std::string const & what, bool passed) {
6370
std::cout << std::left << std::setw(m_width) << what << ": " << (passed ? "PASS" : "** FAIL **") << '\n';
6471
}
6572

@@ -73,7 +80,7 @@ class TestResultPrinter {
7380
// (the arguments in '...') to a pair containing a string representation
7481
// of it and the code itself. That way, print is given a string and a
7582
// value where the string is the code that lead to the value
76-
#define CHECK(...) TestResultPrinter{50}(#__VA_ARGS__, (__VA_ARGS__))
83+
#define CHECK(...) TestResultPrinter{50}.process(#__VA_ARGS__, (__VA_ARGS__))
7784

7885
int main() {
7986

@@ -97,38 +104,28 @@ int main() {
97104

98105
// equivalence & comparison
99106
std::cout<<std::endl;
100-
CHECK(std::is_eq(third<=>Fraction{2,6}));
101-
CHECK(std::is_gt(third<=>Fraction{1,4}));
102-
CHECK(std::is_lt(third<=>Fraction{2,4}));
103-
CHECK(third>Fraction{1,4});
104-
CHECK(third<Fraction{2,4});
105-
CHECK(!(third<=Fraction{1,4}));
106-
CHECK(!(third>=Fraction{2,4}));
107-
CHECK(third>=Fraction{1,4});
108-
CHECK(third<=Fraction{2,4});
109-
CHECK(third>=Fraction{1,3});
110-
CHECK(third<=Fraction{2,3});
111-
CHECK(!(third<Fraction{1,4}));
112-
CHECK(!(third>Fraction{2,4}));
113-
CHECK(!(third<Fraction{1,3}));
114-
CHECK(!(third>Fraction{2,3}));
107+
CHECK(third!=Fraction{2,6});
108+
CHECK(compare(third,Fraction{2,6})==0);
109+
CHECK(compare(third,Fraction{1,4})>0);
110+
CHECK(compare(third,Fraction{2,4})<0);
115111

116112
// multiply
117113
std::cout<<std::endl;
118114
CHECK((third*2)==Fraction{2,3});
119115
CHECK((2*third)==Fraction{2,3});
120-
CHECK(std::is_eq((three*third)<=>Fraction{1,1}));
121-
CHECK(std::is_eq((3*third)<=>Fraction{1,1}));
116+
CHECK(compare(three*third, Fraction{1,1}) == 0);
117+
CHECK(compare(3*third, Fraction{1,1}) == 0);
122118
CHECK((3*third).normalized()==1);
123119

124120
// multiply in place
125121
std::cout<<std::endl;
126122
Fraction one {third};
127123
((one *= 2) *= 3) *= Fraction{1,2};
128-
CHECK(std::is_eq(one<=>1));
124+
CHECK(compare(one, 1)==0);
129125
CHECK(one.normalized()==1);
130126
CHECK(one!=1);
131127

128+
132129
// end
133130
std::cout<<std::endl;
134131

0 commit comments

Comments
 (0)