C++ member variable aliases?
我很确定这是可能的,因为我很确定我已经看到了。我觉得这很好,但我会欣然接受"这是一个糟糕的想法,因为uuuuuuuuuuuuuuuuuuuuuuuuu"。
假设我们有一个基本结构。
1 2 3 4 | struct vertex { float x, y, z; }; |
现在,我想在这些变量上实现别名。
1 2 3 4 5 6 7 | 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; |
理想情况下,第三种语法与数组是不可区分的。也就是说,如果我将
你怎么认为?可能吗?可能但愚蠢?
型
我要做的是制作访问器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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_; } |
型
联盟中的无名称嵌套结构不是标准C++。然而,这应该是有效的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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}; |
。
编辑:更多信息。结构使用指向数据成员的3指针数组访问重载的[]运算符中的数据。
"typedef float vertex::*const vert"一行表示vert是指向顶点结构的float成员的指针。[3]意味着它是其中3个的数组。在重载运算符[]中,此数组被索引,指向数据成员的指针被取消引用并返回值。
此外,不管打包问题是什么,这个方法都可以工作——编译器可以随意填充顶点结构,不管它喜欢什么,它仍然可以正常工作。如果浮点数的包装不同,匿名联合将遇到问题。
型
使用工会?
1 2 3 4 5 6 | union vertex { struct { float x, y, z; }; struct { float r, g, b; }; float arr[3]; }; |
我不推荐-这会导致混乱。
补充:
正如阿德里安在他的回答中所指出的,这个与匿名结构成员的联盟不被ISO C++支持。它在GNU G++中工作(当您打开"EDOCX1"(0))时会抱怨不受支持。这让人想起了标准前C天(k&r 1st edn之前),当时结构元素名称在所有结构中都必须是唯一的,您可以使用约定的符号来获得结构内的偏移量,也可以使用其他结构类型的成员名称-无政府状态的一种形式。当我开始使用C时(很久以前,但在K&R1之后),这已经是历史性的用法了。
C11(ISO/IEC 9899:2011)支持匿名工会成员(两个结构)所示的符号,但C标准的早期版本不支持。ISO/IEC 1488∶2011(C++ 11)的第9.5节提供了匿名联合,但是GNU-EDCOX1〔1〕(4.9·1)不接受用EDCOX1×2表示的代码,标识"EDCOX1×3"。
因为这个想法会导致混乱,所以我并不特别担心它是不标准的;我不会使用这个机制来完成这项任务(即使它是有益的,我也会对在联合中使用匿名结构持谨慎态度)。
提出了一个问题:
The three (x-y-z, r-g-b and the array) do not necessarily align.
号
它是三个元素的联合,三个元素从同一个地址开始。前两个是包含3个浮点值的结构。没有继承,也没有虚拟函数来提供不同的布局等。结构将与连续的三个元素一起布置(实际上,即使标准允许填充)。数组也从相同的地址开始,并且在结构中遵循"无填充"原则,元素与两个结构重叠。我真的不认为会有问题。
参考文献?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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; }; |
型
你可以像其他人提到的那样,通过工会来实现这一点。将颜色和位置重载到同一个结构上可能不是一个好主意(例如,添加两种颜色通常意味着要饱和到1.0,而添加向量是线性的),但是在它们上面覆盖一个float[]是非常好的,也是一种公认的将数据与gl/directx/等交换的方法。
不过,我建议您避免在同一个函数范围内用不同的别名引用同一个成员,因为这会使您陷入一个称为加载命中存储的讨厌的硬件暂停。尤其是,如果您能够:
1 2 3 | vector foo; foo.x = 1.0f; return foo[0] + foo[1]; |
号
以下结构将具有请求的行为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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]; } }; |
型
我不确定我是否正确理解了这个问题。但看起来您需要重载运算符[],以提供对结构/类的类似数组的访问。请参见下面提到的示例:运算符重载
型
我想你可以做一些宏魔术来得到你想要的。但那看起来很难看。为什么要对3种不同类型使用相同的结构、顶点?为什么你不能为颜色定义类?还要记住顶点和颜色不一样。如果您将某个内容更改为顶点,这也会影响颜色,如果您对两者都使用相同的类。
在我看来,这是个坏主意,至少在给出的例子中是这样的:缺点是,对于任何解决方案来说,您可能都能够自由地将"rgb"实例分配给/从"xyz"实例分配出去,这可能很少是明智或正确的。你冒着放弃一些有用的类型安全的风险。
就个人而言,例如您给出的示例,我将从基本
当然,这意味着访问X、Y、Z&R、G、B需要通过访问器(转发到适当的
只是关于使用指向值成员的引用成员的警告。如果复制了这样的对象(如按值传递),则需要定义一个复制构造函数(也可能是赋值运算符)。默认的复制构造函数将为您保留一个副本,该副本的引用成员指向原始对象的值成员,而不是新对象的值成员。这肯定不是你想要的。
正如前面指出的那样,考虑到您最终也会得到更大的对象,我认为使用访问器方法比引用成员更可取。
您可以尝试向变量添加引用,如下所示:
1 2 3 4 5 6 | struct test { float x, y, z; float &r, &g, &b; test() : r(x), g(y), b(z) {} }; |
但是您的结构会变大(从12字节到40字节)。
要对其使用[],请使用前面提到的运算符[]的重载。
我下面有一个模板和两个向量类,一个疯狂,一个理智。该模板实现了一个简单的、在编译时固定的值数组。它是为子类化而设计的,并且使用了一个受保护的数组变量,以避免您为了访问数组而必须跳过环。(有些人可能不喜欢这样的设计。我说,如果您的子类调用重载的操作符,那么耦合可能是一个好主意。)
Crazy类允许您使用名为x、y、z的成员变量,它的作用类似于调用glgetfloatv的数组。SANE只具有访问函数x()、y()、z(),并且仍然可以使用glgetfloatv。您可以使用任何一个类作为可能传递给OpenGL库的其他向量对象的基础。尽管下面的类是特定于点的,但显然您可以只进行搜索/替换,将它们转换为RGB颜色类。
疯狂类是疯狂的,因为用sugar vec.x代替vec.x()的代价是3个引用变量。在大型应用程序中,这可能会占用大量空间。使用更简单、理智的版本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | 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); |