0

So I have a class Rectangle with an overloaded operator= defined as follows:

    Rectangle& Rectangle::operator=(Rectangle &rhs)
{
    if (this != &rhs)
    {
        m_x = rhs.m_x;
        m_y = rhs.m_y;
        m_width = rhs.m_width;
        m_height = rhs.m_height;
        m_intersection = rhs.m_intersection;
    }
    return *this;
}

The compiler doesn't complain so far. But when I attempt to do an assignment my compiler is complaining:

m_boundingBox = Rectangle(x, y, width, height);

Now I'm trying to avoid calling new, hence the assignment this way. I'm assuming I need to implement a separate copy constructor for this kind of assignment, but I'm at a loss as to what the signature should look like.

Here's the compiler error: Error (active) E0349 no operator "=" matches these operands

Firstly, I'd like to fix the above. But secondly, if you can shed some light on the entire subject for this confused code-writing ape, that'd be amazing. Teach a man to fish and all.

Scuba Steve
  • 1,541
  • 1
  • 19
  • 47
  • 4
    Your `operator=` doesn't change the right-hand side, correct? So make it accept a `const Rectangle&` instead of a `Rectangle&` and then it'll be able to accept temporaries as arguments. See [here](https://stackoverflow.com/questions/18565167/non-const-lvalue-references) for a discussion of the underlying issue, although in the context of declaring variables rather than function arguments. – Nathan Pierson Jun 20 '21 at 02:12
  • 6
    Just so you know, a class composed of plain-old-value types and doesn't have any pointers doesn't need an overloaded operator. The default compiler generated assignment operator function will copy each individual member for you. So while the other answers directing you to pass `rhs` as a `const` reference are correct, the simpler solution is to just remove your customer `operator=` method. – selbie Jun 20 '21 at 02:13
  • 2
    In `m_boundingBox = Rectangle(x, y, width, height)`, the right hand side creates a temporary. A temporary can only be bound to a `const` reference. Your copy-assignment operator accepts a non-`const` reference. Change your `operator=()` so it accepts a `const` reference. (Also, separately, if all of the members of `Rectangle` can be assigned (which appears to be the case from how you have defined `Rectangle::operator=()`, then you don't need it at all- the compiler-supplied default `Rectangle::operator=()` is sufficient. – Peter Jun 20 '21 at 02:22
  • @Peter, selbie - do either of you know of any sources/books where I can pick up the more esoteric parts of how the C++ compiler works? I'm a veteran developer at this point, but with not too much C++ under my belt. I'd like to understand more how the language works without stepping through the 'this is a pointer' tutorials online. You've all been very helpful either way, thank you. – Scuba Steve Jun 20 '21 at 02:40
  • 1
    @ScubaSteve If you want to learn more about the default generated constructors: [The rule of three/five/zero](https://en.cppreference.com/w/cpp/language/rule_of_three) – Ranoiaetep Jun 20 '21 at 03:37
  • 1
    @ScubaSteve In terms of books, I think Scott Meyers's *Effective C++* series could be a good one – Ranoiaetep Jun 20 '21 at 03:40
  • @ScubaSteve The advice to not write the user-defined assignment operator is what you should be adhering to. The compiler's default assignment operator not only copies each member automatically without error, it does it efficiently since the compiler is using its own (probably superior) optimized code to make the copy. Also, introducing functions that you do not need to write increases the chances that you will introduce bugs -- the best code is code you don't write. What if there were 20 members, and you fail to copy one of them? You now have a broken assignment operator. – PaulMcKenzie Jun 20 '21 at 05:29
  • @PaulMcKenzie - Yes but what if I want to use these objects inside std data structures that call for implementation of the copy constructor and assignment operator in the documentation. Maybe I can get away with no operator=? But what about the copy constructor? – Scuba Steve Jun 20 '21 at 05:49
  • 1
    @ScubaSteve -- Every class in C++ has a default copy constructor, assignment operator, and destructor. This is a fundamental of C++ that you're missing. Even this: `class foo { };` has a copy constructor, assignment operator, and destructor. The compiler's default copy constructor also makes the copy of each member. As a matter of fact., even the `C` language has default copy semantics for structs. That's why you can pass and return `struct`'s by value, and you will see copies are made. – PaulMcKenzie Jun 20 '21 at 06:12
  • Also `struct s {int x;};` Then -- `int main() { s s1; s1.x = 100; s s2 = s1; }` -- you will see that `s1.x` and `s2.x` are both 100. So you see that the copy is being made. – PaulMcKenzie Jun 20 '21 at 06:20

1 Answers1

2

Your problem is that you haven't declared the argument const. It should be this:

Rectangle& Rectangle::operator=(const Rectangle &rhs)
Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
  • 1
    Yep, that was it. Can you shed some light for me on why that's the case? – Scuba Steve Jun 20 '21 at 02:13
  • 1
    I mean, other than C++ being a cruel mistress. – Scuba Steve Jun 20 '21 at 02:14
  • 1
    @ScubaSteve the literal answer to the "why" question is that the standard prohibits it. If that seems unsatisfactory to you, check out the discussion here: https://stackoverflow.com/questions/1565600/how-come-a-non-const-reference-cannot-bind-to-a-temporary-object – user2407038 Jun 20 '21 at 18:08
  • Even just knowing that these kinds of overloads create temporary objects, and that they can't be referenced without a const, is super helpful. Thanks again. – Scuba Steve Jun 20 '21 at 23:23
  • Note that it isn’t the assignment operator that created the temporary object, rather it’s the Rectangle(x,y,width,height) declaration to the right of ‘m_boundingBox =‘ that did it. – Jeremy Friesner Jun 21 '21 at 01:59