Original Post
I have been working with some user defined classes of my own, and find that operator overloading is very handy and sometimes required. Specifically the use of the std::set class template, since it requires overloads for custom classes to be able to compare them for sorting and equality. In order to use this type you must define your overloads properly to conform to how the class template was implemented.
The difficulty that I encountered was getting the correct const casting on my overloads. I say “const casting” because I don’t know a better way to put it, and I didn’t want to say “const correctness” because I’m not sure if the way that std::set implements them conforms to the current “const correctness” paradigm (if that’s even the right way to put it).
So this lead me to wonder if the way that I was ‘forced’ into implementing the overloads conforms to const correctness, and that it is the way that I should learn to overload, or if it is unique to its situation somehow and should be treated like a special case.
That eventually led me to being less clear on the function of the ‘operator=’ on the variables that it is used on. Here is what I think is correct for the other operators (I’m not going to discuss the Boolean operations here, though).
Operators:
-, +, *, /, %, <<, >>
These operators work on two variables, resulting in a new variable. We do not want to change either of the two given variables. Thus, the input operator will be const, and the function itself will be const. The return value will be the new variable. Given a mental representation of the process as “C=B+A” where A is the parameter to the operator overload function, B is the “this” of the function in the code (that is to say that we can access B’s members directly), and C is the return value. In this example A and B never change, so we want them to both be const in the code. Since classes can take a lot to copy sometimes, we pass A by reference (and because we defined it as const we don’t have to worry about accidently changing it). Then our function would look something like this for “operator+”:
For “operator=” there is only one value to consider, that which is on the right hand side of the equation. There were not very many good results for +”operator=” on google, it seems to strip the equals out and just search for the keyword “operator”. The one place I did get some useful information from basically says that in the “C=B+A” example, “operator=” works as such: “C=B=A”. If it were simply “B=A”, not returning a value, we couldn’t chain assignments as such: “N3 = (N2 = N1)” because if we don’t return our C then “(N2 = N1)” wouldn’t evaluate (it wouldn’t push anything outside the parenthesis) to something passable to N3. Then we are basically setting “this” to the value of the variable ‘A’ and returning a copy of itself (this/B, as C). I guess I demystified my first question just there, because I was unsure of why we had a return value and also self-assignment when it seems like we only had two things to consider (B=A).
Next, my second topic of question related to the same thing. Even though this problem probably just points to a faulty understanding of pointers on my part. Given the syntax:
I read “MyType&” as “address of MyType” meaning that the function is returning a pointer to the type MyType. When I see this I consider it to be always used with a new or malloc type of memory structure, that is, it won’t go out of scope when the function returns, and that it will basically be a pointer to an existing object. Accessing the pointer returned by MyType& and changing the value will result in a change to the value that the pointer is pointing to. This does not sit properly with what we want to accomplish with C=(B=A), because if we change C then we would be changing B. This cannot be right, it is not the behavior that we desire at least. Compounding that problem is that when I see “return *this;” I interpret that as “return a copy of this” because we are dereferencing the pointer causing us to treat it as the type and not a pointer to the type. I would expect that if we were returning the address-of-a-copy-of-this then we defeat our first problem of C changing B, but what of the scope of the variable. I would have to assume that the copy of this would be in the scope of the placement of C and not inside the overloaded operator for B.
I am probably just confusing myself more by trying to answer my own questions as I type this, but I just wanted to clearly express my current though process and find out if I am on the right track. Maybe this will help someone with similar difficulties. Also, why shouldn’t I be returning MyType& for the other operators mentioned? I think it didn’t work for me when I tried, but I could have done it wrong
The difficulty that I encountered was getting the correct const casting on my overloads. I say “const casting” because I don’t know a better way to put it, and I didn’t want to say “const correctness” because I’m not sure if the way that std::set implements them conforms to the current “const correctness” paradigm (if that’s even the right way to put it).
So this lead me to wonder if the way that I was ‘forced’ into implementing the overloads conforms to const correctness, and that it is the way that I should learn to overload, or if it is unique to its situation somehow and should be treated like a special case.
That eventually led me to being less clear on the function of the ‘operator=’ on the variables that it is used on. Here is what I think is correct for the other operators (I’m not going to discuss the Boolean operations here, though).
Operators:
-, +, *, /, %, <<, >>
These operators work on two variables, resulting in a new variable. We do not want to change either of the two given variables. Thus, the input operator will be const, and the function itself will be const. The return value will be the new variable. Given a mental representation of the process as “C=B+A” where A is the parameter to the operator overload function, B is the “this” of the function in the code (that is to say that we can access B’s members directly), and C is the return value. In this example A and B never change, so we want them to both be const in the code. Since classes can take a lot to copy sometimes, we pass A by reference (and because we defined it as const we don’t have to worry about accidently changing it). Then our function would look something like this for “operator+”:
MyType operator+(const MyType &A) const { return MyType(m_Value + A->m_Value);}For “operator=” there is only one value to consider, that which is on the right hand side of the equation. There were not very many good results for +”operator=” on google, it seems to strip the equals out and just search for the keyword “operator”. The one place I did get some useful information from basically says that in the “C=B+A” example, “operator=” works as such: “C=B=A”. If it were simply “B=A”, not returning a value, we couldn’t chain assignments as such: “N3 = (N2 = N1)” because if we don’t return our C then “(N2 = N1)” wouldn’t evaluate (it wouldn’t push anything outside the parenthesis) to something passable to N3. Then we are basically setting “this” to the value of the variable ‘A’ and returning a copy of itself (this/B, as C). I guess I demystified my first question just there, because I was unsure of why we had a return value and also self-assignment when it seems like we only had two things to consider (B=A).
Next, my second topic of question related to the same thing. Even though this problem probably just points to a faulty understanding of pointers on my part. Given the syntax:
MyType& operator=(const MyType &A) { … return *this; }I read “MyType&” as “address of MyType” meaning that the function is returning a pointer to the type MyType. When I see this I consider it to be always used with a new or malloc type of memory structure, that is, it won’t go out of scope when the function returns, and that it will basically be a pointer to an existing object. Accessing the pointer returned by MyType& and changing the value will result in a change to the value that the pointer is pointing to. This does not sit properly with what we want to accomplish with C=(B=A), because if we change C then we would be changing B. This cannot be right, it is not the behavior that we desire at least. Compounding that problem is that when I see “return *this;” I interpret that as “return a copy of this” because we are dereferencing the pointer causing us to treat it as the type and not a pointer to the type. I would expect that if we were returning the address-of-a-copy-of-this then we defeat our first problem of C changing B, but what of the scope of the variable. I would have to assume that the copy of this would be in the scope of the placement of C and not inside the overloaded operator for B.
I am probably just confusing myself more by trying to answer my own questions as I type this, but I just wanted to clearly express my current though process and find out if I am on the right track. Maybe this will help someone with similar difficulties. Also, why shouldn’t I be returning MyType& for the other operators mentioned? I think it didn’t work for me when I tried, but I could have done it wrong