简体   繁体   中英

C++ overloading operator() in struct

Here is a code from b2Math.h from Box2d physics engine.

struct b2Vec2
{   ...
    /// Read from and indexed element.
    float operator () (int i) const
    {
         return (&x)[i];
    }
    /// Write to an indexed element.
    float operator () (int i)
    {
         return (&x)[i];
    }
    ...
    float x, y;
}

Why can't we just use SomeVector.x and SomeVector.y to read/write vector coordinates? And how actually line return (&x)[i]; works? I mean, the array brakets [] after the reference to x component of the struct isn't clear for me.

Thanks in advance for your reply.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM