Is it possible to overload one operator more than once? I have a struct that is just my idea of a C++ Box. I'm trying to increase the size of a box using an integer or another box.

I'm trying to do:

Box X(0, 0, 0, 0);
Box Y(1, 1, 1, 1);

X + 3;  //X should now be  (0, 0, 3, 3);

X + Y;  //X should now be (1, 1, 4, 4);

But I get these errors because the operator isn't defined. But I already defined the + operator for integers yet it says it's not defined. I think I'm mis-using the friend keyword. Should my == operator be friend? The error I get is:
undefined reference to operator+(Box, int)'|

I defined it at the very last 13 lines of the implementation file.

Header File:

struct Box
{
    int X1, Y1, X2, Y2, W, H;
    Box();                                                                               //Default Constructor.
    Box(int X1_, int Y1_, int X2_, int Y2_);                                             //Alternate Constructor.
    Box(Point UpperLeft, Point LowerRight);
    ~Box(){};                                                                           //Destructor.

    Box& operator ()(int x1, int y1, int x2, int y2);

    Box& operator ()(RECT B);

    friend bool operator == (const Box &B, const Box &B2);

    friend inline bool operator != (const Box &B, const Box &B2);

    Box operator += (Box B);

    Box operator -= (Box B);

    Point MidPointBox (const Box &B);

    Box& PointToBox(Point UpperLeft, Point LowerRight);

    Box& operator = (const Box& B);

    bool Contains(Point P) const;

    bool Contains(const Box& B) const;

    friend ostream& operator << (ostream& Str, const Box &B);

    friend inline Box operator + (Box B1, int S);

    friend inline Box operator - (Box B1, int S);
};

My Implementation File:

/**
 *                                              DECLARATION FOR BOXES
 *
 *
**/


Box::Box() : X1(0), Y1(0), X2(0), Y2(0){W = 0; H = 0;}

Box::Box(int X1_, int Y1_, int X2_, int Y2_) : X1(X1_), Y1(Y1_), X2(X2_), Y2(Y2_)
{
    W = (X1 < 0 ? -X1 : X1) + (X2 < 0 ? -X2 : X2); H = (Y2 < 0 ? -Y2 : Y2) + (Y1 < 0 ? -Y1 : Y1);
}

Box::Box(Point UpperLeft, Point LowerRight) : X1(UpperLeft.X), Y1(UpperLeft.Y), X2(LowerRight.X), Y2(LowerRight.Y)
{
    W = (X1 < 0 ? -X1 : X1) + (X2 < 0 ? -X2 : X2); H = (Y2 < 0 ? -Y2 : Y2) + (Y1 < 0 ? -Y1 : Y1);
}

Box& Box::operator ()(int x1, int y1, int x2, int y2)
{
    if ((X1 != x1) && (Y1 != y1) && (X2 != x2) && (Y2 != y2))
    {
        X1 = x1;
        Y1 = y1;
        X2 = x2;
        Y2 = y2;
        W = (X1 < 0 ? -X1 : X1) + (X2 < 0 ? -X2 : X2);
        H = (Y2 < 0 ? -Y2 : Y2) + (Y1 < 0 ? -Y1 : Y1);
    }
    return *this;
}

Box& Box::operator ()(RECT B)
{
    if ((X1 != B.left) && (Y1 != B.top) && (X2 != B.right) && (Y2 != B.bottom))
    {
        X1 = B.left;
        Y1 = B.top;
        X2 = B.right;
        Y2 = B.bottom;
        W = (X1 < 0 ? -X1 : X1) + (X2 < 0 ? -X2 : X2);
        H = (Y2 < 0 ? -Y2 : Y2) + (Y1 < 0 ? -Y1 : Y1);
    }
    return *this;
}

bool operator == (const Box &B, const Box &B2) //Are the Boxes the same?
{
    return ((B.X1 == B2.X1) && (B.X2 == B2.X2) && (B.Y1 == B2.Y1) && (B.Y2 == B2.Y2));
}

inline bool operator != (const Box &B, const Box &B2) //Are the Boxes Not the same?
{
    return !(B == B2);
}

Box Box::operator += (Box B)
{
    X1 += B.X1;
    Y1 += B.Y1;
    X2 += B.X2;
    Y2 += B.Y2;
    W = (X1 < 0 ? -X1 : X1) + (X2 < 0 ? -X2 : X2);
    H = (Y2 < 0 ? -Y2 : Y2) + (Y1 < 0 ? -Y1 : Y1);
    return *this;
}

Box Box::operator -= (Box B)
{
    X1 -= B.X1;
    Y1 -= B.Y1;
    X2 -= B.X2;
    Y2 -= B.Y2;
    W = (X1 < 0 ? -X1 : X1) + (X2 < 0 ? -X2 : X2);
    H = (Y2 < 0 ? -Y2 : Y2) + (Y1 < 0 ? -Y1 : Y1);
    return *this;
}

Point Box::MidPointBox(const Box& B)
{
    return Point(((B.X1 + B.X2)/2), ((B.Y1 + B.Y2)/2));
}

Box& Box::PointToBox(Point UpperLeft, Point LowerRight)
{
    if ((X1 != UpperLeft.X) && (Y1 != UpperLeft.Y) && (X2 != LowerRight.X) && (Y2 != LowerRight.Y))
    {
        X1 = UpperLeft.X;
        Y1 = UpperLeft.Y;
        X2 = LowerRight.X;
        Y2 = LowerRight.Y;
        W = (X1 < 0 ? -X1 : X1) + (X2 < 0 ? -X2 : X2);
        H = (Y2 < 0 ? -Y2 : Y2) + (Y1 < 0 ? -Y1 : Y1);
    }
    return *this;
}

Box& Box::operator = (const Box& B)
{
    if (this != &B)
    {
        X1 = B.X1;
        Y1 = B.Y1;
        X2 = B.X2;
        Y2 = B.Y2;
        W = (X1 < 0 ? -X1 : X1) + (X2 < 0 ? -X2 : X2);
        H = (Y2 < 0 ? -Y2 : Y2) + (Y1 < 0 ? -Y1 : Y1);

    }
    return *this;
}

bool Box::Contains(Point P) const
{
    return (X1 <= P.X && X2 >= P.X && Y1 <= P.X && Y2 >= P.X);
}

bool Box::Contains(const Box& B) const
{
    return (X1 <= B.X1 && X2 >= B.X2 && Y1 <= B.Y1 && Y2 >= B.Y2);
}

ostream& operator << (ostream& Str, const Box &B)//For use with Writeln & cout.
{
    Str<<"("<<B.X1<<", "<<B.Y1<<", "<<B.X2<<", "<<B.Y2<<")";
    return Str;
}

inline Box operator + (Box B1, int S)  //Increase Box Size.
{
    return Box(B1.X1 + S, B1.Y1 + S, B1.X2 + S, B1.Y2 + S);
}

inline Box operator - (Box B1, int S)  //Decrease Box Size.
{
    if (((B1.X1 - S) < 0) || ((B1.Y1 - S) < 0) || ((B1.X2 - S) < 0) || ((B1.Y2 - S) < 0))
        return Box(-1, -1, -1, -1); //Return -1's if its not a valid box.
    else if (((B1.X1 - S) == 0) || ((B1.Y1 - S) == 0) || ((B1.X2 - S) == 0) || ((B1.Y2 - S) == 0))
        return Box(0, 0, 0, 0); //Returns 0's because it becomes a point.
    else
        return Box(B1.X1 + S, B1.Y1 + S, B1.X2 + S, B1.Y2 + S); //Return the resized box.
}

Edited 4 Years Ago by triumphost

Is it possible to overload one operator more than once?

Yes, but only if the parameter types are unique. Operator overloading is no different from regular function overloading in that the signature (not including the return type) must be different for overload resolution to be unambiguous.

Ahh thank you but how do I know whether to do:

BX& operator += (const BX &B)
{
    X1 += B.X1;
    Y1 += B.Y1;
    X2 += B.X2;
    Y2 += B.Y2;
    W = (X1 < 0 ? -X1 : X1) + (X2 < 0 ? -X2 : X2);
    H = (Y2 < 0 ? -Y2 : Y2) + (Y1 < 0 ? -Y1 : Y1);
    return *this;
}

Instead of:

Box operator += (Box B)  //Difference is the Address Of.
{
    X1 += B.X1;
    Y1 += B.Y1;
    X2 += B.X2;
    Y2 += B.Y2;
    W = (X1 < 0 ? -X1 : X1) + (X2 < 0 ? -X2 : X2);
    H = (Y2 < 0 ? -Y2 : Y2) + (Y1 < 0 ? -Y1 : Y1);
    return *this;
}

OR

bool operator == (const Box &B, const Box &B2) //Are the Boxes the same?
{
    return ((B.X1 == B2.X1) && (B.X2 == B2.X2) && (B.Y1 == B2.Y1) && (B.Y2 == B2.Y2));
}

Versus

bool operator == (const BoX &B2) //Are the BoXes the same?
{
    return ((this->X1 == B2.X1) && (this->X2 == B2.X2) && (this->Y1 == B2.Y1) && (this->Y2 == B2.Y2));
}

I read this on C++ Wiki but I don't know when to use which one:

    // binary operator as member function
    Vector2D Vector2D::operator+(const Vector2D& right)const {...}

    // binary operator as non-member function
    Vector2D operator+(const Vector2D& left, const Vector2D& right) {...}

    // binary operator as non-member function with 2 arguments 
    friend Vector2D operator+(const Vector2D& left, const Vector2D& right) {...}

    // unary operator as member function
    Vector2D Vector2D::operator-()const {...}

    // unary operator as non-member function
    Vector2D operator-(const Vector2D& vec) {...}

Edited 4 Years Ago by triumphost

The general rule is this:

  1. Prefer declaring / defining your operator overloads as non-member functions;
  2. If you need access to private members, make them non-member friend functions;
  3. If that particular type of operator must be a member function (like the assignment operator, or conversion operators), then make it a member function.

As for the difference between parameters as (Box B1) and (const Box& B1), well, that is just the difference between pass-by-value and pass-by-reference. Generally, for any non-primitive type (like a class), use the pass-by-reference, e.g., (const Box& B1).

As for your actual error (undefined reference), this is because you declared the operators are inline. When you declare a function as inline, its definition (implementation) must appear in the header file. So, to fix the problem, either you remove the inline keywords from the declarations, or you move the definitions to the header file (e.g., after the class declaration). Either way is fine. If the function is a simple one-liner function, then it might as well be inline and put in the header file, but for longer functions, there is no point in making them inline.

Ahh but the reason I was asking is because if I declare something like:

BoxArray is actually a vector at the core that holds an array of boxes.

BoxArray& Delete(Box &BoxToDelete, bool All = false);

BoxArray BA;
BA<<Box(1, 1, 1, 1)<<Box(2, 2, 2, 2);
BA.Delete(Box(1, 1, 1, 1));  //Will fail as it's not defined.
BA.Delete(B);                //Works perfectly fine.

It will tell me that there is no definition for that operation. I cannot overload Delete twice (one with reference and one without) because it will become ambiguous according to my compiler; which is understandable.

Yet the following works fine with/without the reference:

    BoxArray& Delete(Box BoxToDelete, bool All = false);  //Without reference.

    BoxArray BA;
    BA<<Box(1, 1, 1, 1)<<Box(2, 2, 2, 2);
    BA.Delete(Box(1, 1, 1, 1));  //Delete a box made on the fly.
    Box B(2, 2, 2, 2);
    BA.Delete(B);                //Delete a box pre-constructed.

Anyway I will mark this thread as solved. +Rep+ to both for the great answers.
I will stick to the rule that for arrays leave it as pass by value and for normal variables leave it as pass by value.

Edited 4 Years Ago by triumphost

Pass it by const-reference, as so:

BoxArray& Delete(const Box& BoxToDelete, bool All = false);

That will bound to a temporary too (formally called an "rvalue").

Cannot do that because I iterate it via the size() function from STD::Vector. Thus it throws this error about passing const XXXXXXX as 'this' argument of XXXXXXXXX discards qualifiers.

I use this iteration:

for (int I = 0; I < BoxArrayCore.size(); I++)

and if the Box is found within BoxArrayCore then I remove it with BoxArrayCore.erase(BoxArrayCore.begin() + I);

as BoxArrayCore is my internal vector.

This question has already been answered. Start a new discussion instead.