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

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;
}

Related

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;
}

Do pointer arithmetic expressions count as instructions?

I have written a program that flips the elements of an array that are next to each other. for example
1, 2, 1, 2 becomes 2, 1, 2, 1
The code I wrote is:
#include <iostream>
template <class T>
void swap(T& a, T& b){
T temp = a;
a = b;
b = temp;
}
template <class T>
void oReverse(T* p, int size){
for(T* e = p + size; p < e; p+=1){
swap(*p, *p++);
}
}
int main(){
int arr[] = {1,2,1,2,1,2};
oReverse(arr, 6);
for(int i = 0; i < 6; i++){
std::cout << arr[i] << " ";
}
return 0;
}
The oReverse() function takes care of the pointer magic.
The thing i couldn't understand is: Why does swap(*p, *p++) increment p by one.
At first I wrote p+=2 in the for loop, but it didn't work well, but it does with p++.
I thought the program would swap p and p++ then increment p by 2 then swap p and p++ again and again.
I hope i explained the problem clearly enough.
Firstly, *p++ increments p after the value is fetched. So instead of
swap(*p, *p++);
you would like to do this:
swap(*p, *++p);
But this will not work either. The standard does not define in which order the arguments to a function should be evaluated. So instead of
swap(*p, *++p);
write
swap(*p, *(p+1));
p++;
and you'll be safe. Their behavior are not equivalent, because the evaluation order is unspecified. But their expected behavior are equivalent.
With "expected behavior", I mean the behavior someone who does not know that the evaluations order is not defined by the standard typically would expect.
EDIT:
The evaluation order was unspecified before C++17, but from C++17 it is specified from left to right. But still, don't do things like that. "Clever" constructs have a tendency to cause problems.

valarray divide its element

When I divide a valarray by its first element, only the first element becomes 1 and others keep their original value.
#include <iostream>
#include <valarray>
using namespace std;
int main() {
valarray<double> arr({5,10,15,20,25});
arr=arr/arr[0]; // or arr/=arr[0];
for(int value:arr)cout << value << ' ';
return 0;
}
The actual output is:
1 10 15 20 25
The expected output is:
1 2 3 4 5
Why is the actual output not as expected?
I use g++(4.8.1) with -std=c++11
This one works:
#include <iostream>
#include <valarray>
using namespace std;
int main() {
valarray<double> arr({5,10,15,20,25});
auto v = arr[0];
arr=arr/v; // or arr/=arr[0];
for(int value:arr)cout << value << ' ';
return 0;
}
The problem is that you are trying to use a value (arr[0]) from an array that you are modifying at the same time (arr).
Intuitively, once you have updated arr[0] by doing arr[0]/arr[0], what value does it contain?
Well, that's the value that will be used from now on to divide the other values...
Please, note that the same applies for arr/=arr[0] (first of all, arr[0]/arr[0] takes place, than all the others, in a for loop or something like that).
Also note from the documentation that operator[] of a std::valarray returns a T&. This confirms the assumption above: it is turned to 1 as the first step of your iteration, then all the other operations are useless.
By simply copying it solves the issue, as in the example code.
The details of why this happens are due to implementation tricks used in valarray to improve performance. Both libstdc++ and libc++ use expression templates for the results of valarray operations, rather than performing the operations immediately. This is explicitly allowed by [valarray.syn] p3 in the C++ standard:
Any function returning a valarray<T> is permitted to return an object of another type, provided all the
const member functions of valarray<T> are also applicable to this type.
What happens in your example is that arr/arr[0] doesn't perform the division immediately, but instead it returns an object like _Expr<__divide, _Valarray, _Constant, valarray<double>, double> which has a reference to arr and a reference to arr[0]. When that object is assigned to another valarray the division operation is performed and the result stored directly into the left-hand side of the assignment (this avoids creating a temporary valarray to store the result and then copying it into the left-hand side).
Because in your example the left-hand side is the same object, arr, it means that the reference to arr[0] stored in the expression template refers to a different value once the first element in arr has been updated with the result.
In other words, the end result is something like this:
valarray<double> arr{5, 10, 15, 20, 25};
struct DivisionExpr {
const std::valarray<double>& lhs;
const double& rhs;
};
DivisionExpr divexpr = { arr, arr[0] };
for (int i = 0; i < size(); ++i)
arr[i] = divexpr.lhs[i] / divexpr.rhs;
The first iteration of the for-loop will set arr[0] to arr[0] / arr[0] i.e. arr[0] = 1, and then all subsequent iterations will set arr[i] = arr[i] / 1 which means the values don't change.
I'm considering making a change to the libstdc++ implementation so that the expression template will store a double directly instead of holding a reference. This would mean arr[i] / divexpr.rhs will always evaluate arr[i] / 5 and not use the updated value of arr[i].

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

Overloading post-increment operator

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.