Another Q about operator overloading.

Hi folks,

I'm confused by the example code below from:
http://www.learncpp.com/cpp-tutorial/92-overloading-the-arithmetic-operators/

What is getting me confused:
Alex, the author of Learncpp.com writes about this example:
"The MinMax class keeps track of the minimum and maximum values that it has seen so far. We have overloaded the + operator 3 times, so that we can add two MinMax objects together, or add integers to MinMax objects."

I don't inderstand the "3 times" part. Could someone explain which 3? Because in the addition sum in main(), i see more then 3 addition sums. (i count 5 addtion sums)

Also the code at line 30 has me baffled:
return MinMax(nMin, nMax);
Shouldn't it be returning an addition function, like at line 47?

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
class MinMax
{
private:
    int m_nMin; // The min value seen so far
    int m_nMax; // The max value seen so far
 
public:
    MinMax(int nMin, int nMax)
    {
        m_nMin = nMin;
        m_nMax = nMax;
    }
 
    int GetMin() { return m_nMin; }
    int GetMax() { return m_nMax; }
 
    friend MinMax operator+(const MinMax &cM1, const MinMax &cM2);
    friend MinMax operator+(const MinMax &cM, int nValue);
    friend MinMax operator+(int nValue, const MinMax &cM);
};
 
MinMax operator+(const MinMax &cM1, const MinMax &cM2)
{
    // Get the minimum value seen in cM1 and cM2
    int nMin = cM1.m_nMin < cM2.m_nMin ? cM1.m_nMin : cM2.m_nMin;
 
    // Get the maximum value seen in cM1 and cM2
    int nMax = cM1.m_nMax > cM2.m_nMax ? cM1.m_nMax : cM2.m_nMax;
 
    return MinMax(nMin, nMax);
}
 
MinMax operator+(const MinMax &cM, int nValue)
{
    // Get the minimum value seen in cM and nValue
    int nMin = cM.m_nMin < nValue ? cM.m_nMin : nValue;
 
    // Get the maximum value seen in cM and nValue
    int nMax = cM.m_nMax > nValue ? cM.m_nMax : nValue;
 
    return MinMax(nMin, nMax);
}
 
MinMax operator+(int nValue, const MinMax &cM)
{
    // call operator+(MinMax, nValue)
    return (cM + nValue);
}
 
 
int main()
{
    MinMax cM1(10, 15);
    MinMax cM2(8, 11);
    MinMax cM3(3, 12);
 
    MinMax cMFinal = cM1 + cM2 + 5 + 8 + cM3 + 16;
 
    std::cout << "Result: (" << cMFinal.GetMin() << ", " <<
        cMFinal.GetMax() << ")" << std::endl;
 
    return 0;
}
Last edited on
First time: line 17 / line 22
Second time: line 18 / line 33
Third time: line 19 / line 44

That's all three times.

NOTE: I consider this an abuse of operator overloading. This is bad code.
Last edited on
I agree...
Ahh, i see, so when he says: "We have overloaded the + operator 3 times" what he is actually saying (in my world) is something like: "we have defined 3 scenarios under which the + operator will be overloaded and how the compiler should react." Is that right?

About the bad code: I'm a beginner and am keen to learn correct coding, could you please outline what is wrong with Alex's code?

Thanks! ...........D
Mainly ambiguity of operator+. What minmax + int means? Does it add int to both minimum and maximum? Does it adds that int to set of values you are finding max and min from?

Using common arithmetical operators to denote some special behavior can be surprising to users. If you need operators to reduce visual clutter to comon operations, a bitwise operators often used, especially bitwise_or aka pipe (boos::range composition). Other possibilities are overloading operator() (boost::accumulators) or other operator which has at least some logical connection to operation: / and /= for filename path contacenation (boost::filesystem), % as format specifier (boost::format, but I do not think this was a good choice).
Think of it this way: you have three different functions that all do completely different things, and yet for some reason you decide to give them the same name. Now your code using them is confusing to read because you have to reason about each use and try to figure out which overload is being used. It's a complete mess.

You should only overload a function with one that does the same thing in a slightly different manner, not when it does a completely different thing.
you have three different functions that all do completely different things
Actually they do the same thing: updating minimum and maximum of set of values to reflect minimum and maximum of new union of several sets. Still, plus is not the best choice because of ambigity.
Oh, I don't consider "add all" and "add one" to be the same thing - I guess that's just me.
Last edited on
There is no loss of conceptual integrity if we consider, a la std::basic_string, a single integer to represent an integer interval of width one.
1
2
3
std::string str = "abcd" ;
str += 'e' ;
str += "fgh" ;


Though, operator+ is not ideal: it usually implies some kind of interval union (For example, boost ICL implements operators + and += in terms of aggregated set union.)

If we provide for implicit conversion from 7 to [7] ie. [7 .. 7], a single overloaded operator would suffice.

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
#include <iostream>
#include <algorithm>

struct integer_interval // closed interval
{
    constexpr integer_interval() = default ;
    constexpr integer_interval( int v ) : from_(v), to_(v) {} // note: not explicit
    constexpr integer_interval( int a, int b ) : from_( std::min(a,b) ), to_( std::max(a,b)) {}

    constexpr int from() const { return from_ ; }
    constexpr int to() const { return to_ ; }

    private:
        int from_ = 0 ;
        int to_ = 0 ;
};

// note: somewhat unusual semantics (a finite integer interval is a non-empty set;
//       usually + or | on intervals would imply aggregated set union )
constexpr integer_interval operator+ ( integer_interval a, integer_interval b )
{ return { std::min( a.from(), b.from() ), std::max( a.to(), b.to() ) } ; }

std::ostream& operator<< ( std::ostream& stm, integer_interval r )
{ return stm << "interval [ " << r.from() << " .. " << r.to() << " ]" ; }

int main()
{
    const integer_interval r1( 10, 15 );
    const integer_interval r2( 8, 11 );
    const integer_interval r3( 3, 12 );

    std::cout << r1 + r2 + 5 + 8 + r3 + 16 << '\n' ;
}

http://coliru.stacked-crooked.com/a/63e29e5b2a9c80cb
Topic archived. No new replies allowed.