2016-07-22 56 views
2

我有以下的矢量的類(矢量作爲在空間,而不是陣列):匿名結構與啓用/禁用

template<typename T0, size_t S, typename = typename std::enable_if<std::is_arithmetic<T0>::value && (S > 1 && S < 5)>::type> 
struct Vec 
{ 
    using value_type = T0; 
    using vector_type = Vec<T0, S>; 
    using array_type = std::array<T0, S>; 
    using index_type = size_t; 
    using size_type = size_t; 

    enum { num_components = S }; 

    array_type v; 
}; 

,使得我可以使一個向量類型2,3或4個要素:

template<typename T0> 
    using Vec2 = Vec<T0, 2>; 

    template<typename T0> 
    using Vec3 = Vec<T0, 3>; 

    template<typename T0> 
    using Vec4 = Vec<T0, 4>; 

訪問形式爲v [0],v [1]等(爲了簡潔起見,我不包括[]運算符重載)。有時候我更喜歡x,y等等,但不想要額外的「。」。從命名聯盟中的結構。所以使用Visual Studio 2013的非標準的「功能」,試圖使用匿名聯合,僅使值,如果S(尺寸)爲2,3或4,如下所示:

template<typename T0, size_t S, typename = typename std::enable_if<std::is_arithmetic<T0>::value && (S > 1 && S < 5)>::type> 
    struct Vec 
    { 
     using value_type = T0; 
     using vector_type = Vec<T0, S>; 
     using array_type = std::array<T0, S>; 
     using index_type = size_t; 
     using size_type = size_t; 

     enum { num_components = S }; 

     union 
     { 
      array_type v; 

      template<typename = typename std::enable_if<S == 2>::type> 
      struct 
      { 
       value_type x, y; 
      }; 

      template<typename = typename std::enable_if<S == 3>::type> 
      struct 
      { 
       value_type x, y, z; 
      }; 

      template<typename = typename std::enable_if<S == 4>::type> 
      struct 
      { 
       value_type x, y, z, w; 
      }; 
     }; 
    }; 

不幸的是這給出了我有以下錯誤:

**error C2332: 'struct' : missing tag name** 

而在某種程度上,我想它是。有什麼方法可以實現我在這裏嘗試的嗎?我確定啓用/禁用anoymous結構幾乎肯定會給編譯器一個migrane。我可以像這樣使用匿名聯合,如果我給結構一個名字當然。

+0

它是value_type,所以我可以爲同一元素編寫myvec.v [0]或myvec.x。當然,2D矢量沒有Z,3D矢量沒有W,所以我希望能以某種方式禁用這些功能。 – Robinson

+0

哦,你是對的,對不起。將解決。 – Robinson

回答

2

爲什麼你決定std :: array和struct {T x,T y,... T}的內存佈局是相同的?只有使用#pragma pack將班級的對齊設置重置爲1時纔可以訪問。對於其他人來說,對齊是不可預測的。

你想擁有這樣的類

  1. 通過數據成員選擇等.X,.Y等
  2. 通過操作[]
  3. 確實提供了數據成員的直接訪問,訪問不打破默認數據成員對齊方式(std :: array是線性的,它打破了你的類數據成員的編譯器優化對齊)

以下代碼滿足上述要求非標準功能:

template<typename T, size_t S> 
struct Vec; 
template<typename T> 
struct Vec<T, 1> { 
    enum {count = 1}; 
    T x; 
    T& operator[](size_t i) { 
    assert(i == 0); 
    return x; 
    } 
    const T& operator[](size_t i) const { 
    assert(i == 0); 
    return x; 
    } 
}; 
template<typename T> 
struct Vec<T, 2> { 
    enum { count = 2 }; 
    T x; 
    T y; 
    T& operator[](size_t i) { 
    assert(0 <= i && i < count); 
    return this->*(pointer(i)); 
    } 
    const T& operator[](size_t i) const { 
    assert(0 <= i && i < count); 
    return this->*(pointer(i)); 
    } 
    static T Vec::* pointer(size_t i) { 
    static T Vec::* a[count] = { &Vec::x, &Vec::y }; 
    return a[i]; 
    } 
}; 
template<typename T> 
struct Vec<T, 3> { 
    enum { count = 3 }; 
    T x; 
    T y; 
    T z; 
    T& operator[](size_t i) { 
    assert(0 <= i && i < count); 
    return this->*(pointer(i)); 
    } 
    const T& operator[](size_t i) const { 
    assert(0 <= i && i < count); 
    return this->*(pointer(i)); 
    } 
    static T Vec::* pointer(size_t i) { 
    static T Vec::* a[count] = { &Vec::x, &Vec::y, &Vec::z }; 
    return a[i]; 
    } 
}; 

int main() { 
    Vec<int, 2> v1{ 1, 2 }; 
    assert(v1[0] == v1.x); 
    assert(v1[1] == v1.y); 

    Vec<unsigned char, 3> v2{ 4, 5, 6 }; 
    assert(v2[0] == v2.x); 
    assert(v2[1] == v2.y); 
    assert(v2[2] == v2.z); 
    return 0; 
} 
+0

對齊的好處。參考這裏的信息。 http://stackoverflow.com/questions/8262963/stdarray-alignment。 – Robinson

+0

@Robinson我的觀點是,通常情況下,std :: array的第N個元素的偏移量可能不等於結構的第N個數據成員的偏移量。因此,如果您不將對齊方式重置爲1,則可能會破壞聯合。 – AnatolyS

+0

它已被破壞,這就是爲什麼我正在改變我對待方法的方法(現在就這樣做)。它可以正常工作,因爲我可以將operator + =放在類的外部,並且只是使用T :: count進行迭代,從而具有一個+ =而不是3的函數。我希望編譯器足夠聰明,可以在發佈版本中優化循環。 – Robinson