如何防止使用无名结构体或联合体?

9

我正在构建一个类,其矩阵数据具有联合属性。但是,只有在不给结构体/联合体命名时才能编译通过。然而,在更高级别的警告级别(例如Visual Studio上的四级)下,将会出现警告信息。

warning C4201: nonstandard extension used : nameless struct/union

我仔细研究了一下,似乎找不到防止这种情况发生的方法。我所知道的任何方式都会导致与其中一个声明相关的不同编译错误。如何在不禁用警告的情况下防止出现此警告并符合标准?

    union
    {
        struct
        {
            F32 _11, _12, _13, _14;
            F32 _21, _22, _23, _24;
            F32 _31, _32, _33, _34;
            F32 _41, _42, _43, _44;
        };
        F32 _m[16];
    };

(是的,我知道有可用的矩阵库。请不要将这变成“使用xxx库”的讨论,我正在做这件事以扩展我的C++知识。)


为了日后的读者着想,您能展示一下命名结构体但无法编译的代码吗? - Keith Thompson
5个回答

10

命名似乎是最好的选择。在C++中允许使用匿名联合体,但不允许使用匿名结构体。

union
{
    struct foo
    {
        F32 _11, _12, _13, _14;
        F32 _21, _22, _23, _24;
        F32 _31, _32, _33, _34;
        F32 _41, _42, _43, _44;
    } bar;
    F32 _m[16];
};

你可以使用引用/宏来允许在没有bar的情况下访问。

F32& _11 = bar._11;
F32& _12 = bar._12;

本质上与匿名结构体相同。虽然我不太推荐这样做。如有可能,请使用bar._11


私有/公有(有点像):

struct mat 
{
  struct foo 
  {
    friend class mat;
    private:
      F32 _11, _12, _13, _14;
      F32 _21, _22, _23, _24;
      F32 _31, _32, _33, _34;
      F32 _41, _42, _43, _44;
  };
  union
  {
    foo bar;
    F32 _m[16];
  };
};

看起来已经做到了。虽然不是原问题的一部分,但有没有办法使bar受保护或私有,同时仍允许_m为公共? - mmurphy
哦,好的,很有趣。最后一个问题,我想使用引用(如果可能的话,我想使用_11,不需要前缀)。在类中如何声明该引用?像“F32& _11 = bar._11;”这样的语句不能放在public: 中。 - mmurphy

6
如果您只是想在不更改实际代码的情况下禁用警告,则可以使用#pragma warning指令,如下所示:
#pragma warning(disable : 4201)

如果您想重新启用它,请使用以下方法:

#pragma warning(default : 4201)

如需更多参考信息,请参阅MSDN 文档


抱歉,我应该更具体地说明,并且说不要仅仅禁用警告。 - mmurphy
尽管匿名结构在“C++规范技术上未定义”,但事实上,所有主要的编译器都支持匿名结构,因为C本身就支持它们,而C++需要能够包含来自C的头文件。因此,在我的项目中,这就是我选择的答案。 - Dwayne Robinson

2
union
{
    struct // <- not named here
    {
        F32 _11, _12, _13, _14;
        F32 _21, _22, _23, _24;
        F32 _31, _32, _33, _34;
        F32 _41, _42, _43, _44;
    } AsStruct; // <- named here
    F32 AsArray[16];
};

我修复了它,而不是给结构体类命名,只给实例命名。


0
我见过的最常见的解决方法是通过重载类型的数组运算符来解决。这种方法相当特定于您发布的示例,但在其他情况下似乎没有其他选择的情况下,可以使用类似的方法。
struct Mat {
   F32
    _11, _12, _13, _14,
    _21, _22, _23, _24,
    _31, _32, _33, _34,
    _41, _42, _43, _44;

  
    f32& operator[](uint32 ndx) { 
           return *reinterpret_cast<F32*>(&((&_11)[ndx])); 
     }
    const F32& operator[](uint32 ndx) const { 
           return *reinterpret_cast<const F32*>(&((&_11)[ndx])); 
    }
};

但是,如果有矩阵,通常也会有向量,这可以通过在向量类中定义数组运算符来利用。与上面定义的数组运算符不同,它不返回矩阵数组运算符中的F32引用,而是返回一个向量引用。以下是一个示例:

struct vector4{
    F32 x,y,z,w;
    f32& operator[](uint32 ndx) { 
       return *reinterpret_cast<F32*>(&((&x)[ndx])); 
    }
    const F32& operator[](uint32 ndx) const { 
       return *reinterpret_cast<const F32*>(&((&x)[ndx])); 
    }
};

struct matrix4 {
    
   F32
    _11, _12, _13, _14,
    _21, _22, _23, _24,
    _31, _32, _33, _34,
    _41, _42, _43, _44;


    vector4& operator[](uint32 ndx) { return *reinterpret_cast<vector4*>(&((&_11)[ndx * 4])); }
    const vector4& operator[](uint32 ndx) const { return *reinterpret_cast<const vector4*>(&((&_11)[ndx * 4])); }
};

现在编程可以更自然地书写,而且仍然高效,例如:

 F32 m00 = mymat4[0][0];

或者:

 vector4 col0 = mymat4[0];

这种方法最大的缺点是矩阵现在必须以[col][row]的索引方式进行访问。可以通过向类中添加operator()来解决这个问题...但那是另一个故事了。

-2

你收到的警告不是关于内部结构体,而是关于联合本身。尝试这样做:

union Mat    // <-------
{
    struct
    {
        F32 _11, _12, _13, _14;
        F32 _21, _22, _23, _24;
        F32 _31, _32, _33, _34;
        F32 _41, _42, _43, _44;
    };
    F32 _m[16];
};

Mat mat;
mat._11 = 42;
F32 x = mat._22;
mat._m[ 3 ] = mat._33;

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