我很确定这是可能的,因为我很确定我已经看过它了。我认为这很棒,但我很乐意接受“这是一个可怕的想法,因为____”的答案。
假设我们有一个基本结构。
struct vertex
{
float x, y, z;
};
现在,我想在这些变量上实现别名。
vertex pos;
vertex col;
vertex arr;
pos.x = 0.0f; pos.y = 0.5f; pos.z = 1.0f;
col.r = 0.0f; col.g = 0.5f; col.b = 1.0f;
arr[0] = 0.0f; arr[1] = 0.5f; arr[2] = 1.0f;
理想情况下,第三种语法与数组无法区分。也就是说,如果我将arr
作为参考参数发送给期望其将存储数据的浮点数组的函数(例如,许多OpenGL glGet
函数),它将正常工作。
答案 0 :(得分:22)
我要做的是制作访问者:
struct Vertex {
float& r() { return values[0]; }
float& g() { return values[1]; }
float& b() { return values[2]; }
float& x() { return values[0]; }
float& y() { return values[1]; }
float& z() { return values[2]; }
float operator [] (unsigned i) const { return this->values_[i]; }
float& operator [] (unsigned i) { return this->values_[i]; }
operator float*() const { return this->values_; }
private:
float[3] values_;
}
答案 1 :(得分:13)
使用联盟?
union vertex
{
struct { float x, y, z; };
struct { float r, g, b; };
float arr[3];
};
我不推荐它 - 它会导致混乱。
加:
正如Adrian在他的回答中所指出的那样,ISO C ++不支持这种与匿名结构成员的联合。它适用于GNU G ++(当你打开'-Wall -ansi -pedantic
'时,抱怨不支持)。它让人想起预先标准化的C天(前K& R 1st Edn),当结构元素名称必须在所有结构中都是唯一的时候,你可以使用缩小的符号来获得结构中的偏移量,并且你可以使用其他结构类型的成员名称 - 一种无政府状态。当我开始使用C(很久以前,但后K& R1)时,这已经是历史用法了。
C11(ISO / IEC 9899:2011)支持使用匿名联合成员(对于这两种结构)显示的符号,但不支持早期版本的C标准。 ISO / IEC 14882:2011(C ++ 11)第9.5节规定了匿名联合,但GNU g++
(4.9.1)不接受-pedantic
所示的代码,标识为“{{1 }}”。
由于这个想法会导致混乱,我并不特别担心它不符合标准;我不会使用这个机制来完成这个任务(我会对在联合中使用匿名结构持谨慎态度,即使它是有益的。)
提出了一个问题:
三个(x-y-z,r-g-b和数组)不一定对齐。
这是一个有三个要素的联盟;这三个元素从同一地址开始。前两个是包含3个浮点值的结构。没有继承,并且没有虚拟函数可以提供不同的布局等。结构将以三个连续的元素布局(实际上,即使标准允许填充)。该数组也从相同的地址开始,并且在结构中经历“无填充”,元素与两个结构重叠。我真的没有看到会有问题。
答案 2 :(得分:13)
union中的无名嵌套结构不是标准C ++。但是,这应该有效:
struct Vertex
{
private:
typedef float Vertex::* const vert[3];
static const vert v;
public:
typedef size_t size_type;
float x, y, z;
const float& operator[](size_type i) const {
return this->*v[i];
}
float& operator[](size_type i) {
return this->*v[i];
}
};
const Vertex::vert Vertex::v = {&Vertex::x, &Vertex::y, &Vertex::z};
编辑:更多信息。该struct使用3个指针到数据成员的数组来访问重载[]运算符中的数据。
“typedef float Vertex :: * const vert”这一行意味着vert是指向Vertex结构的float成员的指针。 [3]意味着它是其中3个的数组。在重载的operator []中,索引此数组并取消引用指向数据的成员并返回值。
此外,无论打包问题如何,此方法都可以正常工作 - 编译器可以自由填充顶点结构,但它仍然可以正常工作。如果浮动包装不同,匿名联合将遇到问题。
答案 3 :(得分:3)
你可以通过其他人提到的工会来获得这个。将颜色和位置重载到这样的相同结构上可能不是一个好主意(例如,添加两种颜色通常意味着你想要饱和到1.0,而添加矢量则线性地发生),但是在它们之上覆盖一个float []就像这是非常好的,是一种广为接受的与GL / DirectX /等交换数据的方法。
我建议你避免在相同的函数范围内使用不同的别名引用相同的成员,因为这会将你带入一个令人讨厌的硬件停顿,叫做load-hit-store。特别是,如果可以,请避免这种情况:
vector foo;
foo.x = 1.0f;
return foo[0] + foo[1];
答案 4 :(得分:3)
参考?
template<typename T>
struct vertex {
vertex() :
r(data[0]), g(data[1]), b(data[2]),
x(data[0]), y(data[1]), z(data[2])
{
}
T *operator *() {
return data;
}
const T *operator *() const {
return data;
}
T data[3];
T &r, &g, &b;
T &x, &y, &z;
};
答案 5 :(得分:2)
我猜你可以做一些宏观魔术来获得你想要的东西。 但那看起来很难看。为什么要为3种不同类型使用相同的struct,vertex?为什么不能为颜色定义类? 还要记住,顶点和颜色不一样。如果您将某些内容更改为顶点,那么如果您对两者都具有相同的类,那么这也会影响颜色。
答案 6 :(得分:2)
我不确定我是否正确理解了这个问题。但看起来你需要重载operator []以提供对struct / class的数组访问。请参阅此处提到的示例:Operator overloading
答案 7 :(得分:2)
以下结构将具有所请求的行为:
struct vertex
{
private:
float data[3];
public:
float &x, &y, &z;
float &r, &g, &b;
vertex() : x(data[0]), y(data[1]), z(data[2]), r(data[0]), g(data[1]), b(data[2]) {
}
float& operator [](int i) {
return data[i];
}
};
答案 8 :(得分:1)
我认为不好的想法,至少对于给出的例子:缺点是,对于任何解决方案,你可能会自由地将“rgb”实例分配到/来自“xyz”实例,这可能很少是明智或正确的。即你冒着放弃一些有用的类型安全的风险。
就个人而言,就你给出的例子而言,我将从基类boost::array<float,3>
或类似的子类化rgb和xyz类型。因此它们都继承了operator [],可以传递给期望数组的函数,并且通过更多类型的安全性传递给期望颜色/坐标的东西。通常你想要将xyz或rgb视为一个数组,但很少想要将xyz视为rgb,反之亦然。 (rgb IS-A数组:好的.xyz IS-A数组:好的.rgb IS-A xyz ????我不这么认为!)
当然,这意味着可以访问x,y,z&amp; r,g,b需要通过访问者(转发到适当的operator[](...)
)而不是直接发送给成员。 (你需要C#的属性)。
答案 9 :(得分:0)
下面有一个模板和两个Vector类,一个是疯狂的,一个是理智的。该模板实现了一个简单的固定编译时值数组。它专为子类化而设计,并使用受保护的数组变量来避免必须跳过箍来访问数组。 (有些人可能不喜欢这样的设计。我说,如果你的子类正在调用你的重载运算符,耦合可能是一个好主意。)
疯狂的类允许你有成员变量x,y,z,它就像一个数组,用于调用glGetFloatV。理智的人只有访问函数x(),y(),z(),并且仍然可以使用glGetFloatV。您可以使用任一类作为可能传递给OpenGL库的其他矢量对象的基础。虽然下面的类是特定于点的,但显然你只需要进行搜索/替换就可以将它们变成rgb颜色类。
疯狂的课很疯狂,因为语法糖vec.x而不是vec.x()的成本是3个参考变量。这可能会占用大量应用程序中的大量空间。使用更简单的理智版本。
template <typename T, int N>
class FixedVector {
protected:
T arr[N];
public:
FixedVector();
FixedVector(const T* a) {
for (int i = 0; i < N; ++i) {
arr[i] = a[i];
}
}
FixedVector(const T& other) {
for (int i = 0; i < N; ++i) {
arr[i] = other.arr[i];
}
}
FixedVector& operator=(const T& other) {
for (int i = 0; i < N; ++i) {
arr[i] = other.arr[i];
}
return *this;
}
T* operator&() { return arr; }
const T* operator&() const { return arr; }
T& operator[](int ofs) {
assert(ofs >= 0 && ofs < N);
return arr[ofs];
}
const T& operator[](int ofs) const {
assert(ofs >= 0 && ofs < N);
return arr[ofs];
}
};
class CrazyPoint : public FixedVector<float, 3> {
public:
float &x, &y, &z;
CrazyPoint()
: x(arr[0]), y(arr[1]), z(arr[2])
{ arr[0] = arr[1] = arr[2] = 0.0; }
CrazyPoint(const float* a)
: x(arr[0]), y(arr[1]), z(arr[2])
{
arr[0] = a[0];
arr[1] = a[1];
arr[2] = a[2];
}
CrazyPoint(float a, float b, float c)
: x(a), y(b), z(c)
{
arr[0] = a;
arr[1] = b;
arr[2] = c;
}
};
class SanePoint : public FixedVector<float, 3> {
public:
float& x() { return arr[0]; }
float& y() { return arr[1]; }
float& z() { return arr[2]; }
SanePoint() { arr[0] = arr[1] = arr[2] = 0.0; }
SanePoint(float a, float b, float c)
{
arr[0] = a;
arr[1] = b;
arr[2] = c;
}
};
// usage
SanePoint normal;
glGetFloatV(GL_CURRENT_NORMAL, &normal);
答案 10 :(得分:0)
您可以尝试添加对变量的引用,如下所示:
struct test {
float x, y, z;
float &r, &g, &b;
test() : r(x), g(y), b(z) {}
};
但是你的结构变得更大(从12个字节到40个字节)。
要在其上使用[],请使用operator []的重载,如前所述。
答案 11 :(得分:0)
关于使用指向值成员的引用成员的警告。如果您复制了这样的对象(比如按值传递),则需要定义复制构造函数(也可能还有赋值运算符)。默认的复制构造函数将为您提供一个副本,其引用成员指向原始对象的值成员,而不是新对象的值。这肯定不是你想要的。
考虑到你最终会得到更大的对象,正如已经指出的那样,我认为使用访问器方法比参考成员更受欢迎。