c ++数组元素的别名

时间:2018-01-03 15:12:17

标签: c++ arrays templates alias

我有一个(列)Vector类,它包含一个可以访问的值数组:

Vec<int, 4> v();
v[0] = -2; // <- set first value to -2
v[1] = 1; // <- set second value to 1
....

但这是我的问题:如何为v[0], v[1], v[2], v[3]创建别名?我想将前4个值定义为v.x, v.y, v.z, v.w

Vec<int, 4> v();
v.x = -2; // <- set first value to -2
v.y = 1; // <- set second value to 1
v.z = 4; // <- set third value to 4
v.w = 2; // <- set fourth value to 2

我应该能够分配读取这些值,我不希望它们看起来像一个函数,因此访问第一个值,如:< / p>

Vec<int, 4> v();
v.x() = -2; // <- set first value to -2

不好。最重要的是,矢量类是模板化的,x只应该为dimensions >= 1定义,而y仅适用于dimenions >= 2 ......等等......我如何实现这一目标?

编辑: Vector类与std :: vector无关,它是一个类似于数组的数学向量,因为它具有固定的大小,仅用于数学运算。 (将Vector重命名为Vec)。

我尝试过:

矩阵课程:

template <typename T, size_t ROWS, size_t COLS>
class Matrix {

    public:
        T& operator[] (size_t idx) {return m_matrix[idx];}
        T operator[] (size_t idx) const {return m_matrix[idx];}

    private:
        m_matrix[ROWS * COLS]
};

Vector class:

template <typename T, size_t N>
class Vec: public Matrix<T, 1, N>{

    public:

        T& x() {return (*this)[0];}
        T x() const {return (*this)[0];}

        T& y() {return (*this)[1];}
        T y() const {return (*this)[1];}

        T& z() {return (*this)[2];}
        T z() const {return (*this)[2];}

        T& w() {return (*this)[3];}
        T w() const {return (*this)[3];}
};

这很有效,如果没有为这个维度定义函数,我很容易使用enable_if去除函数,但这在语法上并不令人满意。我尝试过使用引用:

template <typename T, size_t N>
class Vec: public Matrix<T, N, 1>{

    public:

        T& x = (*this)[0];
        T& y = (*this)[1];
        T& z = (*this)[2];
        T& w = (*this)[3];
};

但是这不起作用,它不会给我一个错误,但它也没有正确设置值,当我在设置它们未定义后访问它们时。

编辑nr 2:可能只存在一个更简单的解决方案,当我上次使用Visual Studio社区2015的默认编译器编译引用时,它就可以工作了。但是当我使用GNU GCC编译器在Code :: Blocks中编译它时,它却没有。标准说什么?我的解决方案是否允许使用引用,哪个编译器是错误的?

6 个答案:

答案 0 :(得分:2)

此:

template <typename T, int D> struct Vec;

// You have to manually specialize for all needed sizes
template <typename T> struct Vec<T, 4>
{
    T x, y, z, w;

    T &operator[](int index)
    {
        switch (index)
        {
            default: // throw or something?
            case 0: return x;
            case 1: return y;
            case 2: return z;
            case 3: return w;
        }
    }
    const T &operator[](int index) const
    {
        switch (index)
        {
            default: // throw or something?
            case 0: return x;
            case 1: return y;
            case 2: return z;
            case 3: return w;
        }
    }
};

switch指数不是最优的,但至少它是明确定义的。

对于矩阵,我更喜欢使用Vec<Vec<T, Height>, Width>,这使得mat[x][y]符号起作用。 (如果您愿意,可以交换xy。)

答案 1 :(得分:1)

一种完全不同的方法是重载括号运算符,或者——更简单——使用命名空间和定义的常量:

namespace xyzw {
   constexpr const size_t X = 0;
   constexpr const size_t Y = 1;
   constexpr const size_t Z = 2;
   constexpr const size_t W = 3;
}

namespace rgba {
   constexpr const size_t R = 0;
   constexpr const size_t G = 1;
   constexpr const size_t B = 2;
   constexpr const size_t A = 3;
}

现在,你可以这样使用了:

template<class V>
typename V::value_type snorm3(const V & v) {
  using namespace xyzw;
  if (v[W] == 0) return 0;
  return (v[X] * v[X] + v[Y] * v[Y] + v[Z] * v[Z]) / (v[W] * v[W]);
}

int test() {
  std::vector <int> t{ 0,1,2,3 };
  return snorm3 (t);
}

答案 2 :(得分:0)

如果您接受C ++ 14解决方案,我建议为xyzw创建模板索引包装器,引用{ {1}}变量

T

接下来必须在索引包装 之前继承template <typename T, std::size_t> struct wrapper { wrapper (T const &) {} }; template <typename T> struct wrapper<T, 0U> { T & x; }; template <typename T> struct wrapper<T, 1U> { T & y; }; template <typename T> struct wrapper<T, 2U> { T & z; }; template <typename T> struct wrapper<T, 3U> { T & w; }; 包装器

std::array

现在您可以按如下方式定义帮助template <typename T, std::size_t N> struct arrayWrp { std::array<T, N> arr {}; };

struct VecH

继承自template <typename T, std::size_t ... Is> struct VecH<T, std::index_sequence<Is...>> : public arrayWrp<T, sizeof...(Is)>, public wrapper<T, Is>... { using arrayWrp<T, sizeof...(Is)>::arr; VecH () : arrayWrp<T, sizeof...(Is)>{}, wrapper<T, Is>{ arr[Is] }... { } T & operator[] (std::size_t i) { return arr[i]; } T const & operator[] (std::size_t i) const { return arr[i]; } }; 和所有arrayWrp所需的内容,该链接引用wrapper<T, Is>xyz至{{ 1}},warr[0]arr[1]分别

所以arr[2]成为

arr[3]

以下是一个完整的工作示例

Vec

如果您不喜欢使用template <typename T, std::size_t N> struct Vec : public VecH<T, std::make_index_sequence<N>> { }; 助手#include <array> #include <iostream> #include <type_traits> template <typename T, std::size_t> struct wrapper { wrapper (T const &) {} }; template <typename T> struct wrapper<T, 0U> { T & x; }; template <typename T> struct wrapper<T, 1U> { T & y; }; template <typename T> struct wrapper<T, 2U> { T & z; }; template <typename T> struct wrapper<T, 3U> { T & w; }; template <typename T, std::size_t N> struct arrayWrp { std::array<T, N> arr {}; }; template <typename, typename> struct VecH; template <typename T, std::size_t ... Is> struct VecH<T, std::index_sequence<Is...>> : public arrayWrp<T, sizeof...(Is)>, public wrapper<T, Is>... { using arrayWrp<T, sizeof...(Is)>::arr; VecH () : arrayWrp<T, sizeof...(Is)>{}, wrapper<T, Is>{ arr[Is] }... { } T & operator[] (std::size_t i) { return arr[i]; } T const & operator[] (std::size_t i) const { return arr[i]; } }; template <typename T, std::size_t N> struct Vec : public VecH<T, std::make_index_sequence<N>> { }; int main () { Vec<int, 4U> v4; v4.x = 1; v4.y = 2; v4.z = 3; v4.w = 4; std::cout << "v4: "; for ( auto ui = 0U ; ui < 4U ; ++ui ) std::cout << ' ' << v4[ui]; std::cout << std::endl; Vec<int, 5U> v5; // also over 4 Vec<int, 3U> v3; v3.x = 10; v3.y = 20; v3.z = 30; // v3.w = 40; // compilation error } ,可以使用部分专精化和默认为VecH的模板参数,如下所示

struct

但我不知道这是不是一个好主意:有人可以尝试使用std::make_index_sequence<N>,如下所示

template <typename, std::size_t N, typename = std::make_index_sequence<N>>
struct Vec;

template <typename T, std::size_t N, std::size_t ... Is>
struct Vec<T, N, std::index_sequence<Is...>>
   : public arrayWrp<T, N>, public wrapper<T, Is>...
 {
   using arrayWrp<T, sizeof...(Is)>::arr;

   Vec () : arrayWrp<T, sizeof...(Is)>{}, wrapper<T, Is>{ arr[Is] }...
    { }

   T & operator[] (std::size_t i)
    { return arr[i]; }

   T const & operator[] (std::size_t i) const
    { return arr[i]; }
 };

答案 3 :(得分:0)

作为未经测试的扩展@max66,我们可以以不同的方式定义包装器:

template <typename T, size_t n, class V = std::vector<T> >
class wrapper {
  wrapper(V & vec):v(vec) {}
  operator T & () { return v[n]; }
  operator = (const T & o) { v[n] = o; }
protcted:
   V & v;
}

template <typename T, class V = std::vector<T> > 
class Vec: public V {
public:
  wrapper<T,0,V> x;
  wrapper<T,1,V> y;
  wrapper<T,2,V> z;
  wrapper<T,3,V> w;
  Vec(): x(*this), y(*this), z(*this), w(*this) {} 
} 

在这里,包装器在访问时引用向量(或使用的任何东西)和索引。这可能会更慢,因为 x、y、z、w 不是值而是它们的包装器,您可能需要为它们定义更多运算符。

答案 4 :(得分:0)

可能是这样的,假设 Vec<T>::operator[]() 返回 T& 并假设 Vec 中的元素在内存中按顺序定位:

struct XYZW {
  int x, y, z, w;
};

Vec<int, 4> v;
v[0] = 1;
v[1] = 2;
v[2] = 3;
v[3] = 4;


auto p = reinterpret_cast<XYZW*>(&v[0]);

// use it:
std::cout << p->x << ',' << p->y << ',' << p->z << ',' << p->w << std::endl;

// compare with Vec:
std::cout << v[0] << ',' << v[1] << ',' << v[2] << ',' << v[3] << std::endl;

答案 5 :(得分:-3)

您可以执行以下操作:

// General case uses an array
template<class T, std::size_t N>
class VectorData
{
private:
    T m_data[N];

public:
    T& operator[](int i) { return m_data[i]; }
    const T& operator[](int i) const { return m_data[i]; }
};

// Specializations for various N (4 shown here)
template<class T>
class VectorData<T, 4>
{
public:
    T x, y, z, w;

    T& operator[](int i) { return (&x)[i]; }  // WARNING, see note below
    const T& operator[](int i) const { return (&x)[i]; }
};


template<class T, std::size_t N>
struct Vector : public VectorData<T, N>
{
    // your other Vector stuff here
};

注意:正如以下正确指出的评论者之一,这假设数组元素在内存中的布局与变量列表完全相同(iow,T[4]和{ {1}}部分与{1}}部分兼容)。标准不保证这一点,因此该代码将产生未定义的行为。在实践中,这很好,这样做是更高效的方式。如果您需要可移植的,符合标准的实施方案,则可以选择在struct { T x,y,z,w; }内使用&x[i],这是另一个答案。生成的代码差异可以看作here

如果你真的需要Vector来自Matrix,那么这样的东西仍然是可能的。一般的想法只是你解耦存储和功能。您可以创建一些具有所有功能的通用Matrix类,该类具有其存储的额外模板参数。然后,Vector可以提供自己的存储类型。

类似的东西:

switch