递归类型转换

Posted

技术标签:

【中文标题】递归类型转换【英文标题】: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 是问题所在,所以我正在寻找另一种解决方法。 【参考方案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() 函数中。

以上是关于递归类型转换的主要内容,如果未能解决你的问题,请参考以下文章

通过在运行时在泛型方法中进行类型转换来使用递归函数

仅将 java 类实例转换为原始类型

递归地将python对象图转换为字典

需要处理嵌套列表时如何将递归转换为迭代?

7.内置函数作用域闭包递归

Go -- 在Go语言中使用JSON struct