我有一个(列)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
我应该能够分配和读取值,我不希望它们看起来像一个函数,所以访问第一个值,如:
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]
};
矢量类:
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中编译它时,它却没有。标准说什么?我的解决方案是否允许使用引用,哪个编译器是错的?
这个:
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
ing不是最优的,但至少它是明确定义的。
对于矩阵,我更喜欢使用Vec<Vec<T, Height>, Width>
,这使得mat[x][y]
符号起作用。 (如果你愿意,可以交换x
和y
。)
如果您接受C ++ 14解决方案,我建议为x
,y
,z
和w
创建模板索引包装器,引用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>
,该链接引用x
,y
,z
和w
到arr[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]; }
};
但我不知道这是不是一个好主意:有人可以尝试如下使用Vec
Vec<int, 3U, std::index_sequence<0, 2, 5>> v;
你可以这样做:
// 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
};
注意:正如以下正确指出的评论者之一,这假设数组元素在内存中的布局与T[4]
部分工作的变量列表(iow,struct { T x,y,z,w; }
和&x[i]
是布局兼容的)完全相同。标准不保证这一点,因此该代码将产生未定义的行为。在实践中,这很好,这样做是更高效的方式。如果您需要便携式,标准兼容的实现,您可以选择在switch
中使用VectorData::operator[]
,如另一个答案所示。生成的代码差异可以看作here。
如果你真的需要Vector来自Matrix,那么这样的东西仍然是可能的。一般的想法只是你解耦存储和功能。您可以创建一些具有所有功能的通用Matrix类,该类具有其存储的额外模板参数。然后,Vector可以提供自己的存储类型。
就像是:
// Generic matrix storage
template<class T, std::size_t N>
class MatrixData
{
private:
T m_data[N];
public:
T& operator[](int i) { return m_data[i]; }
const T& operator[](int i) const { return m_data[i]; }
};
// Generic matrix class
template<class T, std::size_t ROWS, std::size_t COLS, class Storage = MatrixData<T, ROWS*COLS>>
class Matrix : public Storage
{
// Matrix functionality here
};
// Specialized storage for Vectors, generic version
template<class T, std::size_t N>
class VectorData : public MatrixData<T, N> { };
// Specialized storage for Vector<T, 4>
template<class T>
class VectorData<T, 4>
{
public:
T x, y, z, w;
T& operator[](int i) { return (&x)[i]; }
const T& operator[](int i) const { return (&x)[i]; }
};
template<class T, std::size_t N>
struct Vector : public Matrix<T, N, 1, VectorData<T, N>>
{
// your other stuff here
};