operators overloading

Keywords: C++

Catalog

1. conclusion

The first two notes are overloadable and no side effects operators in C++. This note is special. It mainly lists two operators that C++ grammar allows overloading, but should not (should not) overload in engineering.

  • Logical operators & & and||
  • A comma operator, a comma expression

The reason why these two operators are not allowed to overload in engineering is that the primitive semantics of operators can not be fully realized after overloading.

2. Logical operator overloading

Let's first recall the primitive semantics of logical operators

  • Operators have only true and false values
  • The final result can be determined without full calculation of the logical expression (short circuit rule)
  • The end result can only be true or false.

C++ grammar allows overloading of logical operators, see the following example code

#include <iostream>
#include <string>

using namespace std;

class Test
{
    int mValue;
public:
    Test(int v)
    {
        mValue = v;
    }
    int value() const
    {
        return mValue;
    }
};

bool operator && (const Test &l, const Test &r)
{
    return l.value() && r.value();
}

bool operator || (const Test &l, const Test &r)
{
    return l.value() || r.value();
}

Test func(Test i)
{
    cout << "Test func(Test i) : i.value() = " << i.value() << endl;

    return i;
}

int main()
{
    Test t0(0);
    Test t1(1);

    /*
     * According to the short circuit rule, the expected correct output is:
     * Test func(Test i) : i.value() = 0
     * Result is false!
    */
    if( func(t0) && func(t1) )
    {
        cout << "Result is true!" << endl;
    }
    else
    {
        cout << "Result is false!" << endl;
    }

    cout << endl;

    /*
     * According to the short circuit rule, the expected correct output is:
     * Test func(Test i) : i.value() = 1
     * Result is true!
    */
    if( func(1) || func(0) )
    {
        cout << "Result is true!" << endl;
    }
    else
    {
        cout << "Result is false!" << endl;
    }

    return 0;
}

The code annotates the correct output we expect, but none of the actual results are up. Why?

  • The essence of operator overloading is to call the function of extended operator by function. The if conditions of 47 and 63 lines are equivalent to operator & (func (t0), func (t1)) and operator | ((func (t1), func (t0)), respectively.
  • The calculation of all parameters must be completed before the overloaded function enters the function body, and the calculation order of the parameters of the function is uncertain.
  • The short-circuit rule is completely invalid, and the original semantics can not be fully realized after the logical operators are overloaded.

Therefore, overloaded logical operators should be avoided in engineering. It is suggested to replace overloaded logical operators by overloaded comparison operators or providing member functions.

3. Comma operator overloading

The comma operator, which can form a comma expression, follows the original semantics of the comma expression

  • Comma expressions are used to connect multiple subexpressions into one expression, such as exp1, exp2, exp3,..., expN
  • The first N-1 subexpression in a comma expression can have no return value, and the last subexpression must have a return value.
  • The final result of a comma expression is the value of the last subexpression
  • Comma expressions compute the values of each subexpression in strict left-to-right order
#include <iostream>
#include <string>

using namespace std;

void func(int i)
{
    cout << "func() : i = " << i << endl;
}

int main()
{
    int a[3][3] =
    {
        (0, 1, 2),   //Comma expression, equivalent to a [3] [3]= {2, 5, 8,};
        (3, 4, 5),
        (6, 7, 8)
    };

    int i = 0;
    int j = 0;

    while (i < 5)
        func(i),    //Comma expression, equivalent to func(i), i + +;

             i++;

    cout << endl;

    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 3; j++)
        {
            cout << a[i][j] << endl;
        }
    }

    cout << endl;

    (i, j) = 6;  //Comma expression, equivalent to j = 6;

    cout << "i = " << i << endl;
    cout << "j = " << j << endl;

    return 0;
}

It is legal to overload comma operators in C++. The parameters of overloaded functions must have a class type, and the return value type of overloaded functions must be a reference.

Class &operator , (const Class &a, const Class &b)
{
    return const_cast<Class &>(b);
}

Look at the following sample code

#include <iostream>
#include <string>

using namespace std;

class Test
{
    int mValue;
public:
    Test(int i)
    {
        mValue = i;
    }
    int value()
    {
        return mValue;
    }
};

Test &operator , (const Test &a, const Test &b)
{
    return const_cast<Test &>(b);
}

Test func(Test &i)
{
    cout << "func() : i = " << i.value() << endl;

    return i;
}

int main()
{
    Test t0(0);
    Test t1(1);

    /*
     * The desired correct output is:
     * func() : i = 0
     * func() : i = 1
     * 1
    */
    Test tt = (func(t0), func(t1));
    cout << tt.value() << endl;

    return 0;
}

Although the output of line 44 is in line with expectations, the output of line 43 is not in line with expectations, for the same reason as the overloading of logical operators, it is caused by the uncertainty of the order in which the function parameters are calculated, that is to say:

  • When the comma operator is overloaded, the comma expression cannot be computed strictly in the order from left to right.
  • Comma operators cannot fully implement native semantics after overloading
  • Therefore, do not overload comma operators in Engineering

Posted by colemanm on Tue, 24 Sep 2019 06:42:57 -0700