【问题标题】:Recursive type casting递归类型转换
【发布时间】:2009-06-01 05:42:46
【问题描述】:

我得到了一个典型的“vector4”类,它带有一个操作符 float* 来为 gl*4fv 和 [] 自动转换它。 还有用于编译器优化的“const”版本以及 const 引用,这很好用:

typedef struct vec4
{
    ...

    // ----------------------------------------------------------------- //

        // Cast operator, for []
    inline operator float* () { 
        return (float*)this;
    }

        // Const cast operator, for const []
    inline operator const float* () const { 
        return (const float*)this;
    }

    // ----------------------------------------------------------------- //

    ...

        // Vertex / Vector 
    struct {
        float x, y, z, w;
    };  

            // Color
    struct {
        float r, g, b, a;
    };
} vec4;

我的问题是当我现在编写一个“matrix4”类时,使用支持的运算符 vec4* 从矩阵中提取行,并且还具有矩阵[][] 运算符的“副作用”,这很好。

typedef struct mat4
{
    ...

    // ----------------------------------------------------------------- //
            // Cast operator, for []
    inline operator vec4* () { 
        return (vec4*)this;
    }

        // Const cast operator, for const []
    inline operator const vec4* () const { 
        return (const vec4*)this;
    }
    // ----------------------------------------------------------------- //

    private:
        float f[16];
} mat4;

我的问题是,为什么编译器没有检测到转换 mat4 浮动*?我怀疑 mat4 -> vec4 -> float* 的继承是合理的,但似乎并非如此。 我想到编译器可能会将其视为未定义的 mat4 -> vec4* -> float*, 但是这个假设是无效的,因为定义了操作符

inline operator const vec4 () const { 
    return (vec4)*this;
}

不起作用,并调用 glMultMatrixf(mat4(...)); (例如)产生与没有运算符时相同的错误消息。

在 mat4 中定义操作符 float* 当然是不可能的,因为这将消除使用 [][] 的能力(二义性操作符)

对此有任何解决方案吗?还是每次我想自动转换为浮动*时都必须手动转换为 vec4?自动投射是一个非常好的功能,它可以巧妙地使用 OpenGL 插入代码。

【问题讨论】:

  • inline 在类/结构声明中不需要关键字。
  • 如果有一种方法可以为您的问题进行自动转换,那么 [][] 就已经存在一个模棱两可的运算符问题,就像您在 mat4 中定义了运算符 float* 一样。
  • GCC 4.3.x 似乎没有问题,它使用 operator[] 作为 [][] 和 operator const float* 作为 const float* 接受功能。 MSVC 是问题所在,所以我正在寻找另一种解决方法。

标签: c++ casting


【解决方案1】:

C++ 可以执行自动转换,但按照标准不会执行两次连续的自动转换。

它被认为太有利于无意中的错误和歧义。

三个可能对您有用的选项:

当您需要矩阵中的float* 时,自己显式执行第一次转换。

FuncThatWantsFloatPointer( *static_cast<Vec4*>(MyMatrix) );

在您的矩阵类中实现到float* 的直接转换。

typedef struct mat4
{
        ...
        operator const float* () const
        { 
                return *static_cast<Vec4*>(*this);
        }

} mat4;

如果您喜欢使用方括号表示法,请在矩阵和向量类中实现 operator[]

typedef struct mat4
{
        ...
        const vec4& operator[] ( size_t index ) const
        { 
                return static_cast<Vec4*>(*this)[index];
        }

} mat4;

【讨论】:

  • 您可以将第一个转换设为显式,这样只有一个转换是隐式的。 ((Vec4)MyMatrix) 可以转换为float*
  • 您还可以在矩阵类中添加一个运算符,通过先将其转换为 Vec4* 将其直接转换为 float*。
  • @Shmoopty,这两种方法都行不通。 'explicit' 关键字仅适用于构造函数,添加 float* 将不允许我使用假运算符 [][] (mat[]vec[]float)。
  • 我指的不是关键字。我只是在我的回答中澄清了。希望对您有所帮助。
【解决方案2】:

... 其中一个规则是不允许任何转换序列包含多个用户定义的转换(即,对单个争论构造函数或隐式类型转换运算符的调用)。 - 更有效的 C++,Scott Meyers

您可能希望为 vec4 和 mat4 重载 operator[]。

struct vec4
{
    float& operator[](int index) { return f[index]; }
    const float& operator[](int index) const { return f[index]; }

    operator float*() { return f; }
    operator const float*() const { return f; }

    float f[4];
};

struct mat4 
{
    vec4& operator[](int row) { return v[row]; }
    const vec4& operator[](int row) const { return v[row]; }

    operator float*() { return f; }
    operator const float*() const { return f; }

    union
    {
        vec4 v[4];
        float f[16];
    };
};

int main(void)
{
    mat4 m;
    ::memset(&m, 0, sizeof(mat4));
    m[0][1] = 1;
    cout << m[0][1] << endl; // it prints 1.

    return 0;
}

【讨论】:

  • 这将削弱结构 - 根本没有 '[const] float*' 转换,这对于类似 gl*4fv 的函数来说会很好。我尝试重载运算符,这对 GCC 工作正常,但 MSVC 错误和错误。虽然我并不真正关心 MSVC,但我仍然支持可移植代码。
  • 我只是想展示如何重载 operator[]。您可以轻松添加这些操作的 const 版本。我用 MSVC 2008 测试了代码,结果很好。不过,不确定 GCC。
  • 运算符 const float*() const { return f; } // 像这样。 :)
  • 这将消除双 [][]。 (你的 = mat[15],我想要 = mat[3][3]);
  • 嗯,我不明白你的意思。你仍然可以这样做 m[0][1] = 1;这已经在 main() 函数中。
猜你喜欢
  • 2016-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多