Overloading post-increment operator - c++

MyClass MyClass::operator++(int) {
return ++(*this);
}
That's the code I have written. I works correctly, but all tutorials say that I have to create a temporary object and return it:
MyClass MyClass::operator++(int) {
MyClass tmp = *this;
++(*this);
return tmp;
}
Please tell me which way is the best?

The first version is wrong, because it returns the new value. The postincrement operator is supposed to return the old value.

Second One!
Post Increment means that the variable is incremented after the expression is evaluated.
Simple example:
int i = 10;
int j = i++;
cout<<j; //j = 10
cout<<i; // i = 11
Your First example would make j = 11, which is incorrect.

The tutorials are correct.
Your version returns the wrong value. The post-increment operator is supposed to return the previous value, not the new value. Check for yourself with a plain old int:
int x = 5;
int y = x++;
cout << x << y << endl; // prints 56, not 66.

That's due to the definition of post-increment operator.
post-increment operator: Increments AFTER the value is used.
pre-increment operator: Increments BEFORE the value is used.
So if you do it your way, the value returned from the function is the incremented one.
The tutorials increment the object itself, but return a non incremented COPY of the object.

Related

Visual studio 2019 Non-legit optimization in map::operator[]? [duplicate]

I just encountered a weird bug in my code (C++14) caused by the unexpected (for me at least) behavior of std::map. Here's a simple example demonstrating the behavior:
#include <iostream>
#include <map>
int main()
{
std::map<int, int> m;
for(int i = 0; i < 3; ++i) {
m[m.size()] = m.size();
}
for(const std::pair<int, int>& e : m) {
std::cout << e.first << " " << e.second << std::endl;
}
return 0;
}
This prints:
0 1
1 2
2 3
I was expecting:
0 0
1 1
2 2
What is going on here? The map first adds a new element with first set and only then (when the map's size has already increased) sets second? I don't quite understand why this would make sense. Or is there some other explanation? Thanks!
There is a few thing that happens in the expression
m[m.size()] = m.size();
First, m[m.size()] and = m.size() need to be evaluated. In C++14, that evaluation order is indeterminately sequenced. m[m.size()] might happen first, or it might happen second. If it happens first, then you see the results you received. If it happens second, then you get the output that you expected.
If you want
0 0
1 1
2 2
Then you need to guarantee that ordering yourself. You can use map::insert() to do just that:
m.insert(m.size(), m.size());
Starting in C++17, this is no longer the case. The standard was changed to say:
The assignment operator (=) and the compound assignment operators all group right-to-left. All require a modifiable lvalue as their left operand; their result is an lvalue referring to the left operand. The result in all cases is a bit-field if the left operand is a bit-field. In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression. The right operand is sequenced before the left operand. With respect to an indeterminately-sequenced function call, the operation of a compound assignment is a single evaluation.
It now guarantees that = m.size() happens before m[m.size()], and you get the order that you expect.
Let's write down the loop code in a more elaborate way:
for(int i = 0; i < 3; ++i) {
int& temp = m.operator[](m.size());
temp = m.size();
}
Note that calling operator[] already does the insertion of an default initialized element before you assign a value to it. Therefore, the map has already grown by the time the right hand side of the assignment is evaluated.
To fix the issue, make sure the size is determined before you use operator[]:
for(int i = 0; i < 3; ++i) {
size_t v = m.size();
m[v] = v;
}

Inserting its own size to std::map

I just encountered a weird bug in my code (C++14) caused by the unexpected (for me at least) behavior of std::map. Here's a simple example demonstrating the behavior:
#include <iostream>
#include <map>
int main()
{
std::map<int, int> m;
for(int i = 0; i < 3; ++i) {
m[m.size()] = m.size();
}
for(const std::pair<int, int>& e : m) {
std::cout << e.first << " " << e.second << std::endl;
}
return 0;
}
This prints:
0 1
1 2
2 3
I was expecting:
0 0
1 1
2 2
What is going on here? The map first adds a new element with first set and only then (when the map's size has already increased) sets second? I don't quite understand why this would make sense. Or is there some other explanation? Thanks!
There is a few thing that happens in the expression
m[m.size()] = m.size();
First, m[m.size()] and = m.size() need to be evaluated. In C++14, that evaluation order is indeterminately sequenced. m[m.size()] might happen first, or it might happen second. If it happens first, then you see the results you received. If it happens second, then you get the output that you expected.
If you want
0 0
1 1
2 2
Then you need to guarantee that ordering yourself. You can use map::insert() to do just that:
m.insert(m.size(), m.size());
Starting in C++17, this is no longer the case. The standard was changed to say:
The assignment operator (=) and the compound assignment operators all group right-to-left. All require a modifiable lvalue as their left operand; their result is an lvalue referring to the left operand. The result in all cases is a bit-field if the left operand is a bit-field. In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression. The right operand is sequenced before the left operand. With respect to an indeterminately-sequenced function call, the operation of a compound assignment is a single evaluation.
It now guarantees that = m.size() happens before m[m.size()], and you get the order that you expect.
Let's write down the loop code in a more elaborate way:
for(int i = 0; i < 3; ++i) {
int& temp = m.operator[](m.size());
temp = m.size();
}
Note that calling operator[] already does the insertion of an default initialized element before you assign a value to it. Therefore, the map has already grown by the time the right hand side of the assignment is evaluated.
To fix the issue, make sure the size is determined before you use operator[]:
for(int i = 0; i < 3; ++i) {
size_t v = m.size();
m[v] = v;
}

Can a function in C++ return something and then execute its code?

When doing something like this:
i2 = i++;
The ++ operator will return i, and then it will increment i by one.
Can a function also return something and then execute its code?
No, once the scope of a function is over (at the closing }), no further code can be executed.
However, the function can store the old state of the input, modify the input, and return the old value of the input. This gives the effect of executing code after the function returns a value. As an example:
int f(int &n) {
int x = n; // store input
n = 42; // input is modified before return
return x; // old input is returned
}
int b = f(a); // b is equal to a
// but now a is 42
As you have observed, post-increment is an example of where such behavior is useful. Another example would be std::exchange, which gives the appearance of modifying the input after returning a value.
If you were to implement postfix increment by yourself, you would first store the original value, then use increment but still return the original value:
Number Number::operator++ (int)
{
Number ans = *this;
++(*this);
return ans;
}
You may check this FAQ for more details: https://isocpp.org/wiki/faq/operator-overloading#increment-pre-post-overloading
So there is no function code execution after return in C++.

Increment pointer returned by function

Hey I was experimenting a bit with C/C++ and pointers
while reading stuff here
I made myself a function to return a pointer to the int at some place in a global array.
int vals[] = { 5, 1, 45 };
int * setValue(int k) {
return &vals[k];
}
However I was able to do this
int* j = setValue(0);
j++;
*j = 7;
to manipulate the array
but that:
*(++setValue(0)) = 42;
din't work.
Notice however *setValue(0) = 42; works
From what I understand I call the function and get some pointer I increment it to make it point to the 2nd element in my array. Lastly I deference the pointer and assign a new value to the integer it pointed to.
I find C++ pointers and references can be somewhat confusing but maybe someone can explain me this behavior.
EDIT:
This question is NOT a duplicate of Increment, preincrement and postincrement
because it is not about pre- vs. post-increment but rather about increment on pointers that are the return of a function.
EDIT2:
Tweaking the function
int ** setValue(int k) {
int* x = &vals[k];
return &x;
}
You can use
*(++(*setValue(1))) = 42;
You can't call a unary operator (++) on something that is not a variable. setValue(0) is treated as a value.
So,
*(setValue(0)++) = 42;
should be
*(setValue(0) + 1) = 42;

std::vector operator==: value or reference comparison?

So, I was trying to come up with a solution for When does x==x+2 in C++ on codegolf, and came up with this snippet only to realize that I don't know how it works. I'm not sure why both of these conditions evaluate to true.
Does anyone know if the line labeled line: is true because x==&x or because x+2 is evaluated before the left-hand side of ==?
#include <iostream>
#include <vector>
std::vector<int>& operator+ ( std::vector<int> &v, int val )
{
v.push_back(val);
return v;
}
int main()
{
std::vector<int> x;
std::vector<int> y = x + 2; // y is a copy of x, and x is [2]
// how are both of these are true?
std::cout << (x==y) << "\n"; // value comparison [2]==[2]
line:
std::cout << (x==x+2) << "\n"; // reference comparison? &x == &(x+2)
// not sure if this is relevant
std::cout << (x+2==x) << "\n"; // also true
return 0;
}
It seems--since vectors appear to be compared by value--that if x were evaluated before x+2, then x wouldn't be equal to x+2 (by value). I'm probably missing something obvious. Thanks in advance.
For the standard containers, operator== is overloaded as std::equal. This in turn works on itera­tors and applies comparison by dereferncing, as in *it1 == *it2. Therefore, no copies are required.
The expression x == x + 2 is the same as operator==(x, x + 2). Both operands are evaluated before the function call, and since x + 2 modifies x, both operands are the same. Thus the equality holds.
The surprise is the result of your unconventional design choice in overloading the +-operator. This is ge­ne­rally poor practice and a taboo in any collaborative project. If you absolutely must overload opera­tors, then only if they behave as expected, follow established semantics and are not surprising. The usu­al behaviour of the +-operator is to return a new object, by value, and leave the operand unaffected. Like so:
std::vector<int> operator+(std::vector<int> v, int n)
{
v.push_back(n);
return v;
}
std::vector's equality comparison performs a lexicographical compare that checks that the size of lhs and rhs are the same, and then compares element by element.
The problem with your code is that you are assigning x+2 to y, and your addition operator is modifying the lhs, acting like a += operator.
Here:
std::vector<int> y = x + 2;
this modifies x, and copy assigns y from x. A well behaved operator+ would be something like
std::vector<int> operator+ ( std::vector<int> v, int val )
{
v.push_back(val);
return v;
}
The confusion arises from the unconventional definition of +. Normally, it would return a modified copy of its argument, leaving the argument itself unchanged. Since the operator acts more like +=, modifying its argument and returning a reference to it, this is roughly equivalent to:
x.push_back(2), x == x
comparing the modified vector to itself.
C++ always compares values, never references; if you want a reference comparison, then you must explicitly compare addresses, &x == &y.
std::vector::operator==() is (usually?) a function, and this is a sequence point in C++03, which means that it needs to fully evaluate all of it's parameters before it can be called.
Both parameters resolve to the same reference (vector x), so it is only natural that it would evaluate to true.
This is because that operator+ never creates a new object, it just modifies one.
It is, in fact, equivalent to this code:
std::vector<int> x;
x.push_back(2);
std::cout << (x==x) << "\n"; // this is no surprise