Here is a code from b2Math.h from Box2d physics engine.
There appears to be an error in the copy and paste of the Box2D source code that you've posted. Particularly, there appears to be a missing ampersand in the non-const method.
Also, this code snippet appears to from a codebase other than the current 2.3.2 released code.
Here's the portion from the Box2D 2.3.2 sources on GitHub:
/// Read from and indexed element.
float32 operator () (int32 i) const
{
return (&x)[i];
}
/// Write to an indexed element.
float32& operator () (int32 i)
{
return (&x)[i];
}
Why can't we just use SomeVector.x and SomeVector.y to read/write vector coordinates?
We can, and we usually do.
There is however some code in Box2D (b2AABB::RayCast
specifically) that appears to have been written from an algorithm (the comment for b2AABB::RayCast
saying "From Real-time Collision Detection, p179") that iterates over x and y as array subscripts zero and one. I'd guess that Erin Cato (the author of Box2D) implemented these operators this way: (a) to make access stylistically conform more to the algorithm, (b) to make it work, and (c) to make it work in a way that appears efficient. I can confirm that it does at least work.
I have my own fork of Box2D in which I've rewritten these operators. I changed its interface to using []
(instead of ()
), and its implementation to using a switch
statement to explicitly access either x
or y
. The latter I did to clearly-to-me avoid potentially undefined behavior w.r.t. the C++ standard.
Here's a snippet of the relevant portions of what my implementation looks like instead (and please note that I do not claim this to be perfect or good even, only that its an alternative implementation that works and that it should clearly be relying on defined behavior):
/// Accesses element by index.
/// @param i Index (0 for x, 1 for y).
auto operator[] (size_type i) const
{
assert(i < max_size());
switch (i)
{
case 0: return x;
case 1: return y;
default: break;
}
return x;
}
/// Accesses element by index.
/// @param i Index (0 for x, 1 for y).
auto& operator[] (size_type i)
{
assert(i < max_size());
switch (i)
{
case 0: return x;
case 1: return y;
default: break;
}
return x;
}
And how actually line return (&x)[i]; works?
As I implied above, I have looked into this exact question before.
It works when the memory layout of the x and y member variables of the structure is the same as having an array of two floats; which it typically does. So the ampersand (&
) takes the address of the x
parameter and then treats that address like an address to the start of an array which it then indexes by i
.
I don't think this is defined behavior however as far as the C++ standard is concerned though. It wasn't clearly defined enough for my taste however.
Hope this answers your questions.