2015-08-15 84 views
2

由於讀了很多警告不要使用工會這樣的:訪問像數組一樣的成員?

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

但事有湊巧,有時我確實需要在使用索引的循環中訪問rg,ba,否則我必須分別爲每個組件複製相當長的代碼。

於是我想出了這一點:

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]
+0

你爲什麼不使用則'的std ::陣列',應該是安全的,並足夠 – coincoin

+0

快,因爲訪問它的成分,除非大家都在項目知道顏色模型是BGRA,我需要引入'enum'←見2.3節。 –

+0

反過來這樣做通常會更容易:保留一個包含所有rgba組合的'uint32_t',並提供像'uint8_t r()'等訪問器。在'uint8_t == unsigned char'的情況下,你甚至可以使用別名'uint32_t'帶'uint8_t',àla'uint8_t&r()'。 – dyp

回答

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

+0

'static_assert(sizeof(rgba)== 4,「!」);'可以用來解決這個特定的問題。儘管這並不能確保程序會在任何地方進行編譯,但它確保了這個問題不會發生在它編譯的C++實現上。 – dyp

+0

*「不可能與constexpr」*它實際上是可能的,並且是在常量表達式中報告錯誤的首選方法:http://melpon.org/wandbox/permlink/Uedhwayol6ZrykD6其實,'assert'在這裏具有相同的效果在常量表達式上,雖然我不確定這是否便攜。 – dyp

+0

儘管我應該補充一點,C++ 11 constexpr函數中不允許使用「switch」。從C++ 14開始允許。並且'assert'在C++ 11中是不允許的,可能('assert'的實現沒有被指定爲AFAIK,但是C++ 11的constexpr函數是非常嚴格的)。 – dyp

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