访问数组之类的成员?

时间:2015-08-15 09:23:20

标签: c++ c++11 strict-aliasing type-punning

由于阅读了很多警告,不使用这样的工会:

union rgba
{
    struct
    {
        uint8_t r, g, b, a;
    } components;
    uint8_t index[4];
    uint32_t value;
};

因为这是未定义的行为,所以我决定保持简单:

struct rgba
{
    uint8_t r, g, b, a;
};

但碰巧有时我确实需要使用索引在循环中访问rgba,否则我必须复制漂亮的每个组件的长代码。

所以我想出了这个:

struct rgba
{
    u8 r, g, b, a;

    constexpr u8& operator[](size_t x)
    {
        return const_cast<u8*>(&r)[x];
    }
};

这取决于rgba在内存中以线性方式放置的假设,其间没有隐藏的样板,并且编译器保留变量顺序。

有了这个,我可以像我想要的那样访问组件:

rgba c;
for (int i = 0; i < 3; i++)
    c[i] = i ^ (i + 7);
c.a = 0xFF;
  1. 由于我做了很大的假设,我很确定这比使用联合用于类型惩罚更加不确定。我是对的吗?
  2. 我怎样才能实现类似的设计?
    1. 我希望尽可能避免写c.r() = 5,因为它看起来很有趣。
    2. 拥有c.components[RED]之类的访问器使用宏,我喜欢避免使用宏。
    3. 当考虑访问此类枚举所需的名称空间时,使用枚举替换来自第2点的宏将显得难看。想象一下c.components[Image::Channels::Red]

2 个答案:

答案 0 :(得分:2)

该标准为您提供了有关问题1的答案:

  

9.2 / 15:分配具有相同访问控制的类的非静态数据成员,以便以后的成员具有更高的地址   在一个类对象中。非静态数据的分配顺序   具有不同访问控制的成员是未指定的。 <强>实施   对齐要求可能导致两个相邻的成员不在   紧接着分配; 所以可能要求   用于管理虚拟功能和虚拟基类的空间。

有很多选项可以回答问题2.如果您喜欢数组符号:

  • 为什么不使用switch()安全地返回对正确元素的引用。
  • 或更好,为什么不用真正的数组替换你的成员?

第一个看起来像:

struct rgba2
{
    uint8_t r, g, b, a;
    constexpr uint8_t& operator[](size_t x)
    {
        assert(x>=0 && x<4);
        switch (x){
            case 0: return r; 
            case 1: return g;  
            case 2: return b;  
            case 3: return a; 
            //default: throw(1);  // not possible with constexpr
        }
    }
};

第二个:

struct rgba3
{
    enum ergb { red, green, blue, alpha};
    uint8_t c[alpha+1];
    constexpr uint8_t& operator[](ergb x)
    {
        assert(x>=0 && x<4);
        return c[x];
    }
};

live demo

答案 1 :(得分:1)

您可以通过使用指向成员的静态指针数组,以符合标准的方式有效地执行此操作。

内存开销是每个类一个数组。生成的代码与在编译时以优化的构建知道索引时的直接成员访问相同。

以下是一些示例代码(Source):

#include <iostream>

template<class T>
class Vector3
{
public:
   Vector3(const T &xx, const T &yy, const T &zz) :
      x(xx), y(yy), z(zz)
   {
   };

   T& operator[](size_t idx)
   {
      return this->*offsets_[idx];
   };

   const T& operator[](size_t idx) const
   {
      return this->*offsets_[idx];
   };

public:
   T x,y,z;
private:
   static T Vector3::* const offsets_[3];
};

template<class T>
T Vector3<T>::* const Vector3<T>::offsets_[3] =
{
   &Vector3<T>::x,
   &Vector3<T>::y,
   &Vector3<T>::z
};

int main()
{
   Vector3<float> vec(1,2,3);
   vec[0] = 5;
   std::cout << vec.x << std::endl;
}