std::vector operator==: value or reference comparison? - c++

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

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

Why pointer subtraction is undefined behavior in C++?

For the example below, what may cause undefined behavior? and why?
#include <cstddef>
#include <iostream>
template <typename Ty>
bool in_range(const Ty *test, const Ty *r, size_t n)
{
return 0 < (test - r) && (test - r) < (std::ptrdiff_t)n;
}
void f() {
double foo[10];
double *x = &foo[0];
double bar;
std::cout << std::boolalpha << in_range(&bar, x, 10);
}
I have not found the answer in When is pointer subtraction undefined in C?
Pointer arithmetic, including the subtraction of two pointers, is only defined if the pointers point to elements within the same array, or one past the end of that array. In this context, a scalar counts as an array of size 1.
It's pretty pointless to allow pointer arithmetic in any other instance. To do that would unnecessarily constrain the C's memory model and could curtail its flexibility and ability to port to exotic architectures.
For the code, as you've written it, the answer is basically the same for C++ as it is for C: you get defined behavior if and only if the two pointers involved refer to parts of the same array, or one past its end (where, as #bathsheba already noted, a non-array object is treated as being the same as an array of one item).
C++ does, however, add one wrinkle that might be useful to know about here: even though neither subtraction nor ordered comparisons (e.g., <) is required to produce meaningful results when applied to pointers to separate objects, std::less<T> and friends, from <functional> are required to do so. So, given two separate objects like this:
Object a;
Object b;
...comparing the addresses of the two objects with the comparison objects must "yield a strict total order that is consistent among those specializations and is also consistent with the partial order imposed by the built-in operators <, >, <=, >=." (N4659, [comparisons]/2).
As such, you could write your function something like this:
template <typename Ty>
bool in_range(const Ty *test, const Ty *begin, const Ty *end)
{
return std::less_equal<Ty *>()(begin, test) && std::less<Ty *>()(test, end);
}
If you really want to maintain the original function signature, you could do that as well:
template <typename Ty>
bool in_range(const Ty *test, const Ty *r, size_t n)
{
auto end = r + n;
return std::less_equal<Ty *>()(r, test) && std::less<Ty *>()(test, end);
}
[Note that I've written it using std::less_equal for the first comparison, and std:less for the second to match the typically expected semantics of C++, where the range is defined as [begin, end). ]
This does carry one proviso though: you need to ensure that r points to to the beginning of an array of at least n items1, or else the auto end = r + n; will produce undefined behavior.
At least for what I'd expect as the typical use-case for such a function, you can probably simplify usage a little but by passing the array itself, rather than a pointer and explicit length:
template <class Ty, size_t N>
bool in_range(Ty (&array)[N], Ty *test) {
return std::less_equal<Ty *>()(&array[0], test) &&
std::less<Ty *>()(test, &array[0] + N);
}
In this case, you'd just pass the name of the array, and the pointer you want to test:
int foo[10];
int *bar = &foo[4];
std::cout << std::boolalpha << in_range(foo, bar) << "\n"; // returns true
This only supports testing against actual arrays though. If you attempt to pass a non-array item as the first parameter it simply won't compile:
int foo[10];
int bar;
int *baz = &foo[0];
int *ptr = new int[20];
std::cout << std::boolalpha << in_range(bar, baz) << "\n"; // won't compile
std::cout << std::boolalpha << in_range(ptr, baz) << "\n"; // won't compile either
The former probably prevents some accidents. The latter might not be quite as desirable. If we want to support both, we can do so via overloading (for all three situations, if we choose to):
template <class Ty, size_t N>
bool in_range(Ty (&array)[N], Ty *test) {
return std::less_equal<Ty *>()(&array[0], test) &&
std::less<Ty *>()(test, &array[0]+ N);
}
template <class Ty>
bool in_range(Ty &a, Ty *b) { return &a == b; }
template <class Ty>
bool in_range(Ty a, Ty b, size_t N) {
return std::less_equal<Ty>()(a, b) &&
std::less<Ty>()(b, a + N);
}
void f() {
double foo[10];
double *x = &foo[0];
double bar;
double *baz = new double[20];
std::cout << std::boolalpha << in_range(foo, x) << "\n";
std::cout << std::boolalpha << in_range(bar, x) << "\n";
std::cout << std::boolalpha << in_range(baz, x, 20) << "\n";
}
1. If you want to get really technical, it doesn't have to point to the beginning of the array--it just has to point to part of an array that's followed by at least n items in the array.
Undefined behavior in this case usually does not cause a crash, but a meaningless or inconsistent result.
In most modern architectures, subtracting 2 unrelated pointers just computes the difference of addresses divided by the size of the pointed type, approximately this:
A *p1, *p2;
...
ptrdiff_t diff = ((intptr_t)p2 - (intptr_t)p1) / (intptr_t)sizeof(*p1);
Examples of architectures where the behavior would be unexpected are Intel's 16 bit segmented medium and large models:
These models were once prevalent on PCs, before the 386 came along and its 32-bit model.
far pointers were stored in 2 parts: a 16-bit segment (or selector in protected mode) and a 16-bit offset.
comparing 2 pointers for equality required 2 separate compare instructions and conditional jumps for the segment and the offset.
comparing a pointer to NULL was usually optimized as a single comparison of the segment part with 0
subtracting 2 pointers and comparing for relative position was performed on the offset part only, making the silent assumption that both pointers pointed to the same array, hence had the same segment.
in your example, both objects have automatic storage, so they are both in the same segment, pointed to as SS, but for 2 objects allocated from the heap you could have p <= q && q <= p and p != q at the same time, or p - q == 0 with p != q which is covered by undefined behavior.

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].

Is this a known pitfall of C++11 for loops?

Let's imagine we have a struct for holding 3 doubles with some member functions:
struct Vector {
double x, y, z;
// ...
Vector &negate() {
x = -x; y = -y; z = -z;
return *this;
}
Vector &normalize() {
double s = 1./sqrt(x*x+y*y+z*z);
x *= s; y *= s; z *= s;
return *this;
}
// ...
};
This is a little contrived for simplicity, but I'm sure you agree that similar code is out there. The methods allow you to conveniently chain, for example:
Vector v = ...;
v.normalize().negate();
Or even:
Vector v = Vector{1., 2., 3.}.normalize().negate();
Now if we provided begin() and end() functions, we could use our Vector in a new-style for loop, say to loop over the 3 coordinates x, y, and z (you can no doubt construct more "useful" examples by replacing Vector with e.g. String):
Vector v = ...;
for (double x : v) { ... }
We can even do:
Vector v = ...;
for (double x : v.normalize().negate()) { ... }
and also:
for (double x : Vector{1., 2., 3.}) { ... }
However, the following (it seems to me) is broken:
for (double x : Vector{1., 2., 3.}.normalize()) { ... }
While it seems like a logical combination of the previous two usages, I think this last usage creates a dangling reference while the previous two are completely fine.
Is this correct and Widely appreciated?
Which part of the above is the "bad" part, that should be avoided?
Would the language be improved by changing the definition of the range-based for loop such that temporaries constructed in the for-expression exist for the duration of the loop?
Is this correct and Widely appreciated?
Yes, your understanding of things is correct.
Which part of the above is the "bad" part, that should be avoided?
The bad part is taking an l-value reference to a temporary returned from a function, and binding it to an r-value reference. It is just as bad as this:
auto &&t = Vector{1., 2., 3.}.normalize();
The temporary Vector{1., 2., 3.}'s lifetime cannot be extended because the compiler has no idea that the return value from normalize references it.
Would the language be improved by changing the definition of the range-based for loop such that temporaries constructed in the for-expression exist for the duration of the loop?
That would be highly inconsistent with how C++ works.
Would it prevent certain gotchas made by people using chained expressions on temporaries or various lazy-evaluation methods for expressions? Yes. But it would also be require special-case compiler code, as well as be confusing as to why it doesn't work with other expression constructs.
A much more reasonable solution would be some way to inform the compiler that the return value of a function is always a reference to this, and therefore if the return value is bound to a temporary-extending construct, then it would extend the correct temporary. That's a language-level solution though.
Presently (if the compiler supports it), you can make it so that normalize cannot be called on a temporary:
struct Vector {
double x, y, z;
// ...
Vector &normalize() & {
double s = 1./sqrt(x*x+y*y+z*z);
x *= s; y *= s; z *= s;
return *this;
}
Vector &normalize() && = delete;
};
This will cause Vector{1., 2., 3.}.normalize() to give a compile error, while v.normalize() will work fine. Obviously you won't be able to do correct things like this:
Vector t = Vector{1., 2., 3.}.normalize();
But you also won't be able to do incorrect things.
Alternatively, as suggested in the comments, you can make the rvalue reference version return a value rather than a reference:
struct Vector {
double x, y, z;
// ...
Vector &normalize() & {
double s = 1./sqrt(x*x+y*y+z*z);
x *= s; y *= s; z *= s;
return *this;
}
Vector normalize() && {
Vector ret = *this;
ret.normalize();
return ret;
}
};
If Vector was a type with actual resources to move, you could use Vector ret = std::move(*this); instead. Named return value optimization makes this reasonably optimal in terms of performance.
for (double x : Vector{1., 2., 3.}.normalize()) { ... }
That is not a limitation of the language, but a problem with your code. The expression Vector{1., 2., 3.} creates a temporary, but the normalize function returns an lvalue-reference. Because the expression is an lvalue, the compiler assumes that the object will be alive, but because it is a reference to a temporary, the object dies after the full expression is evaluated, so you are left with a dangling reference.
Now, if you change your design to return a new object by value rather than a reference to the current object, then there would be no issue and the code would work as expected.
IMHO, the second example is already flawed. That the modifying operators return *this is convenient in the way you mentioned: it allows chaining of modifiers. It can be used for simply handing on the result of the modification, but doing this is error-prone because it can easily be overlooked. If I see something like
Vector v{1., 2., 3.};
auto foo = somefunction1(v, 17);
auto bar = somefunction2(true, v, 2, foo);
auto baz = somefunction3(bar.quun(v), 93.2, v.qwarv(foo));
I wouldn't automatically suspect that the functions modify v as a side-effect. Of course, they could, but it would be confusing. So if I was to write something like this, I would make sure that v stays constant. For your example, I would add free functions
auto normalized(Vector v) -> Vector {return v.normalize();}
auto negated(Vector v) -> Vector {return v.negate();}
and then write the loops
for( double x : negated(normalized(v)) ) { ... }
and
for( double x : normalized(Vector{1., 2., 3}) ) { ... }
That is IMO better readable, and it's safer. Of course, it requires an extra copy, however for heap-allocated data this could likely be done in a cheap C++11 move operation.