在结构体中使用C++重载运算符()

4
这里是来自Box2d物理引擎的b2Math.h代码。
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;
}

为什么我们不能只使用SomeVector.x和SomeVector.y来读写向量坐标?实际上,return (&x)[i];这一行是如何工作的呢?我的意思是,在引用结构体的x分量之后,方括号[]中的数组不清楚。

感谢您的回复。


6
除了 i 等于 0 的情况外,(&x)[i] 会导致未定义行为,因为它会尝试在未分配给这样一个数组的内存中进行数组索引操作。 - Cory Kramer
1
修改operator()中存在错误:应该返回float& - iksemyonov
1
看起来你缺少重要的信息来完全回答这个问题。然而,“为什么我们不能只使用SomeVector.x和SomeVector.y来读写向量坐标?”很容易,你可以 :) - George
@Code-Apprentice 不是保证的(可能存在填充),即使它们彼此相邻,您仍然访问指针不拥有的内存,这是未定义行为。 - NathanOliver
@user402700,这仍然没有展示我在第一条评论中所问的SomeVector的声明。我猜你的意思是你有一个变量b2Vec2 SomeVector。正如其他人已经说过的那样,你可以像你说的那样使用它:SomeVector.xSomeVector.y - Code-Apprentice
显示剩余8条评论
1个回答

0
这是来自Box2d物理引擎的b2Math.h代码。
您发布的Box2D源代码复制粘贴中似乎存在错误。特别是,在非const方法中似乎缺少一个“&”符号。
此外,此代码片段似乎来自当前2.3.2发布代码之外的代码库。
以下是来自GitHub上Box2D 2.3.2源代码的部分内容:
/// 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];
}

为什么我们不能只使用SomeVector.x和SomeVector.y来读写向量坐标?
我们可以这样做,而且我们通常也这样做。
然而,在Box2D中有一些代码(特别是b2AABB::RayCast),它似乎是从一个算法编写的(b2AABB::RayCast的注释说“来自实时碰撞检测,p179”),该算法迭代x和y作为数组下标零和一。我猜Erin Cato(Box2D的作者)以这种方式实现了这些运算符:(a)使访问在风格上更符合算法,(b)使其工作,并且(c)使其以看起来高效的方式工作。我可以确认它至少可以工作。

我有自己的Box2D分支,我已经重写了这些运算符。我改变它的接口使用[](而不是()),并将其实现更改为使用switch语句明确访问xy。我之所以这样做,是为了避免相对于C++标准可能存在未定义行为。

以下是相关部分的代码片段(请注意,我并不认为这是完美的或好的,但它是一种可行的替代实现,并且应该明确依赖于定义行为):

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

正如我上面暗示的,我之前研究过这个问题。
当结构体的 x 和 y 成员变量的内存布局与拥有两个浮点数数组的布局相同时,它就起作用了;通常情况下都是这样的。因此,取地址符号(&)获取 x 参数的地址,然后将该地址视为数组的起始地址,再通过 i 进行索引。
但是我认为这不是定义良好的行为,至少在 C++ 标准中没有明确规定。对于我来说,这并不够清晰明了。
希望这回答了你的问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接