C++ 成员变量别名?

发布于 2024-07-13 10:59:49 字数 549 浏览 7 评论 0原文

我很确定这是可能的,因为我很确定我已经看到它完成了。 我认为这很棒,但我很乐意接受“这是一个糟糕的主意,因为 ____”这样的答案。

假设我们有一个基本结构。

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 函数),它会工作得很好。

你怎么认为? 可能的? 可能但很愚蠢?

I'm pretty sure this is possible, because I'm pretty sure I've seen it done. I think it is awesome, but I will gladly accept answers along the lines of "this is a terrible idea because ____".

Say we have a basic struct.

struct vertex
{
    float x, y, z;
};

Now, I want to implement aliases on these variables.

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;

Ideally the third syntax would be indistinguishable from an array. That is, if I sent arr as a reference parameter to a function expecting an array of floats into which it will store data (eg many of the OpenGL glGet functions), it would work fine.

What do you think? Possible? Possible but stupid?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

仙气飘飘 2024-07-20 10:59:50

正如其他人提到的,您可以通过工会获得此服务。 像这样将颜色和位置重载到相同的结构上可能不是一个好主意(例如,添加两种颜色通常意味着您想要饱和到 1.0,而添加向量是线性发生的),但是在它们上面覆盖一个 float[] ,就像这是非常好的并且是与 GL/DirectX/等交换数据的一种被广泛接受的方法。

不过,我建议您避免在同一函数作用域中通过不同别名引用同一成员,因为这会让您陷入称为加载命中存储的令人讨厌的硬件停顿。 特别是,如果可以的话,请避免这种情况:

vector foo; 
foo.x = 1.0f;
return foo[0] + foo[1];

You can get this with a union as others have mentioned. Overloading color and position onto the same structure like this may not be a good idea ( for example, adding two colors usually means you want to saturate to 1.0, whereas adding vectors happens linearly ), but overlaying a float[] on top of them like that is perfectly fine and a well accepted means of interchanging data with GL/DirectX/etc.

I recommend you avoid referring to the same member by different aliases in the same function scope, though, because this will drive you into a nasty hardware stall called a load-hit-store. In particular, avoid this if you can:

vector foo; 
foo.x = 1.0f;
return foo[0] + foo[1];
一梦等七年七年为一梦 2024-07-20 10:59:50

以下结构将具有所请求的行为:

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];
    }
};

Following structure will have the requested behavior:

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];
    }
};
筑梦 2024-07-20 10:59:50

我想你可以做一些宏观魔法来得到你想要的。
但这会看起来很难看。 为什么要对 3 种不同类型使用相同的结构、顶点? 为什么不能定义颜色类别?
另请记住,顶点和颜色不同。 如果您更改顶点的某些内容,如果两者具有相同的类,这也会影响颜色。

I guess you can do some macro magic to get what you want.
But that will look ugly. Why do you want to use same struct, vertex for 3 different types? Why can't you define class for color?
Also keep in mind that vertex and color are not same. If you change something to vertex, that will affect the color also, if you have the same class for both.

北城挽邺 2024-07-20 10:59:50

我不确定我是否正确理解了这个问题。 但看起来您需要重载运算符[]以提供对结构/类的类似数组的访问。 请参阅此处提到的示例:运算符重载

I am not sure whether I understood the question correctly. But it looks like you need to overload the operator[] to provide array like access to your struct/class. See the example mentioned here: Operator overloading

人生戏 2024-07-20 10:59:50

我有一个模板和下面两个 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);

I have a template and two Vector classes below, one crazy, one sane. The template implements a simple fixed at compile time array of values. It is designed for subclassing and uses a protected array variable to avoid you having to jump through hoops to access the array. (Some folks might not like such a design. I say, if your subclasses are calling your overloaded operators, coupling might be a good idea.)

The crazy class allows you to have member variables called x, y, z and it acts like an array for calls to glGetFloatV. The sane one just has accessor functions x(), y(), z() and still works with glGetFloatV. You can use either class as a basis for other vector objects you might pass to the OpenGL library. Although the classes below are specific to points, you can obviously just do a search/replace to turn them into a rgb color classes.

The crazy class is crazy because the cost of the syntactic sugar vec.x instead of vec.x() is 3 reference variables. That could take up a lot of space in a large application. Use the simpler sane version.

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);
九局 2024-07-20 10:59:50

您可以尝试添加对变量的引用,如下所示:

struct test {
        float x, y, z;
        float &r, &g, &b;

        test() : r(x), g(y), b(z) {}
    };

但是您的结构会变得更大(从 12 字节到 40 字节)。

要在其上使用 [],请使用运算符 [] 的重载,如前所述。

You can try adding references to variables, like this:

struct test {
        float x, y, z;
        float &r, &g, &b;

        test() : r(x), g(y), b(z) {}
    };

But your structure gets bigger (from 12 bytes to 40 bytes).

To use [] on it, use overloading of operator[], as mentioned before.

记忆消瘦 2024-07-20 10:59:50

在我看来,至少对于给出的示例来说,这是一个坏主意:缺点是,对于任何解决方案,您可能都能够自由地将“rgb”实例分配给“xyz”实例,或者从“xyz”实例分配“rgb”实例,这可能很少是明智或正确的。 即你可能会放弃一些有用的类型安全性。

就我个人而言,对于您给出的示例,我将从基本 boost::array 或类似类型中继承 rgb 和 xyz 类型。 因此它们都继承了operator[],可以传递给需要数组的函数,并以更多的类型安全性传递给需要颜色/坐标的东西。 您经常希望将 xyz 或 rgb 视为数组,但很少希望将 xyz 视为 rgb,反之亦然。 (rgb IS-A 数组:好的。xyz IS-A 数组:好的。rgb IS-A xyz ????我不这么认为!)

当然,这意味着访问 x,y,z & r,g,b 需要通过访问器(转发到适当的operator[](...))而不是直接到成员。 (为此您需要 C# 的属性)。

Bad idea in my opinion, at least for the example given: the downside is that, for just about any solution to this, you're probably going to be able to freely assign "rgb" instances to/from "xyz" instances, which is probably rarely sensible or correct. ie you risk giving up some useful type safety.

Personally, for the example you give, I'd subclass rgb and xyz types from a base boost::array<float,3> or similar. So both of them inherit operator[], can be passed to functions expecting arrays, and passed with more type safety to things expecting colours/coordinates. It's often you want to treat an xyz or an rgb as an array, but rare you want to treat an xyz as an rgb or vice-versa. (rgb IS-A array: OK. xyz IS-A array: OK. rgb IS-A xyz ???? I don't think so!)

Of course that means access to x,y,z & r,g,b needs to be by accessor (forwarding to the appropriate operator[](...) ) rather than direct to the member. (You'd need C#'s properties for that).

无声无音无过去 2024-07-20 10:59:50

只是关于使用指向值成员的引用成员的警告。 如果您复制这样的对象(例如按值传输它),您需要定义一个复制构造函数(可能还需要定义赋值运算符)。 默认的复制构造函数将为您留下一个副本,其引用成员指向原始对象的值成员,而不是新对象的值成员。 这当然不是你想要的。

考虑到您最终也会得到更大的对象,正如已经指出的那样,我认为使用访问器方法优于引用成员。

Just a warning about using reference members pointing to value members. You need to define a copy constructor (and possibly also assignment operator), if you ever copy such an object (like transfer it by value). The default copy constructor will leave you with a copy whose reference members point to the value members of the original object, not the ones of the new object. This is certainly not something you want.

Considering you also end up with larger objects, as already pointed out, I think using accessor methods is to be preferred over reference members.

演出会有结束 2024-07-20 10:59:50

我认为发帖人正在寻找一些非常简单的东西,没有性能开销 - 正如您所希望的 3D 矢量类之类的东西。 因此,添加虚拟函数(vtable 间接成本)、附加成员(内存成本)、联合(每个新名称可能需要所有用户重新编译),甚至预处理器魔法(增加程序大小、跨类型等效性)都是不可取的。

现实世界的用例是采用模板化的 Vector3 类(可以是基于双精度或基于浮点的),并以用户友好的方式将其应用到其他场景。 它可能是用 [x, y, z] 成员定义的,但如果您想将其用于旋转,您可能需要 [psi, theta, phi],用于速度 [dx, dy, dz] 等。

对于整个类型,您可以在编译时使用以下内容为其指定别名:
使用 Rotation3 = Vector3;

但似乎没有什么比为基础变量别名化更简单或更高效的事情了,对吧?

I think the poster was looking for something very simple, with no performance overhead - as you would want with something like a 3D vector class. So adding virtual functions (vtable indirection cost), additional members (memory cost), unions (each new name may require a recompile of all users), or even preprocessor magic (increased program size, cross-type equivalency) is undesirable.

Real world use case would be to take a templated Vector3 class (which could be double-based or float-based) and apply it to other scenarios in a user-friendly manner. It is likely defined with [x, y, z] members, but if you want to use it for rotations, you might want [psi, theta, phi], for speeds [dx, dy, dz], etc.

For the entire type, you can use the following to alias it at compile time:
using Rotation3 = Vector3;

But there appears to be nothing as simple or performant that you can do for aliasing the underlying variables, right?

攒眉千度 2024-07-20 10:59:50

在工作中遇到类似的问题,但无法更改原始数据类型。
采用这个解决方案:

struct Vec3
{
    int x;
    int y;
    int z;
};

struct Color : public Vec3
{
    Color(Vec3){};
    int& r() {return x;}
    int& g() {return y;}
    int& b() {return z;}
};
    
int main()
{
    Vec3 a = {1,2,3};
    auto& b = static_cast<Color&>(a);
    std::cout << a.x << "\n";
    auto& red = b.r();
    red += 10;
    std::cout << red << " " << a.x << "\n";
    return 0;
}

Had a simular problem at work, but could not change the original data type.
Went with this solution:

struct Vec3
{
    int x;
    int y;
    int z;
};

struct Color : public Vec3
{
    Color(Vec3){};
    int& r() {return x;}
    int& g() {return y;}
    int& b() {return z;}
};
    
int main()
{
    Vec3 a = {1,2,3};
    auto& b = static_cast<Color&>(a);
    std::cout << a.x << "\n";
    auto& red = b.r();
    red += 10;
    std::cout << red << " " << a.x << "\n";
    return 0;
}
来日方长 2024-07-20 10:59:49

我要做的是制作访问器:

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_;
}

What I would do is make accessors:

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_;
}
零度℉ 2024-07-20 10:59:49

联合中的无名嵌套结构不是标准 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};

编辑:更多信息。 该结构使用 3 个指向数据成员的指针的数组来访问重载的 [] 运算符中的数据。

“typedef float Vertex::* const vert”行表示 vert 是指向 Vertex 结构的 float 成员的指针。 [3] 意味着它是一个由 3 个这样的数组组成的数组。 在重载的operator[]中,该数组被索引,指向数据成员的指针被取消引用并返回值。

此外,无论打包问题如何,此方法都应该有效 - 编译器可以随意填充顶点结构,但它仍然可以正常工作。 如果浮点数的包装方式不同,匿名联合将会遇到问题。

Nameless nested structs in a union are not standard C++. This, however, should work:

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};

EDIT: A little more information. The struct uses an array of 3 pointer-to-data-members to access the data in the overloaded [] operators.

The line "typedef float Vertex::* const vert" means that vert is a pointer to a float member of the Vertex struct. The [3] means that it's an array of 3 of these. In the overloaded operator[], this array is indexed and the pointer-to-data-member is dereferenced and the value returned.

Additionally, this method should work regardless of packing issues - the compiler is free to pad the Vertex structure however it likes and it'll still work just fine. An anonymous union will run into problems if the floats are packed differently.

罪歌 2024-07-20 10:59:49

使用工会?

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,标识“警告:ISO C++ 禁止匿名结构 [-Wpedantic]”。

由于这个想法会导致混乱,所以我并不特别担心它不标准; 我不会使用该机制来完成此任务(并且我对在联合中使用匿名结构持谨慎态度,即使它是有益的)。


有人提出了一个担忧:

三者(xyz、rgb 和数组)不一定对齐。

它是三个要素的结合; 这三个元素从同一地址开始。 前两个是包含 3 个浮点值的结构。 没有继承,也没有虚函数来提供不同的布局等。结构将通过三个连续元素进行布局(实际上,即使标准允许填充)。 该数组也从相同的地址开始,并且在结构中“无填充”的情况下,元素与两个结构重叠。 我真的不认为会有问题。

Use a union?

union vertex
{
    struct { float x, y, z; };
    struct { float r, g, b; };
    float arr[3];
};

I wouldn't recommend it - it will lead to confusion.


Added:

As noted by Adrian in his answer, this union with anonymous struct members is not supported by ISO C++. It works in GNU G++ (with complaints about not being supported when you turn on '-Wall -ansi -pedantic'). It is reminiscent of the pre-pre-standard C days (pre-K&R 1st Edn), when structure element names had to be unique across all structures, and you could use contracted notations to get to an offset within the structure, and you could use member names from other structure types - a form of anarchy. By the time I started using C (a long time ago, but post-K&R1), that was already historical usage.

The notation shown with anonymous union members (for the two structures) is supported by C11 (ISO/IEC 9899:2011), but not by earlier versions of the C standard. Section 9.5 of ISO/IEC 14882:2011 (C++11) provides for anonymous unions, but GNU g++ (4.9.1) does not accept the code shown with -pedantic, identifying "warning: ISO C++ prohibits anonymous structs [-Wpedantic]".

Since the idea will lead to confusion, I'm not particularly concerned that it isn't standard; I would not use the mechanism for this task (and I'd be leery of using anonymous structures in a union even if it was beneficial).


A concern was raised:

The three (x-y-z, r-g-b and the array) do not necessarily align.

It is a union with three elements; the three elements start at the same address. The first two are structures containing 3 float values. There's no inheritance and there are no virtual functions to give different layouts, etc. The structures will be laid out with the three elements contiguous (in practice, even if the standard permits padding). The array also starts at the same address, and subject to 'no padding' in the structures, the elements overlap the two structures. I really don't see that there would be a problem.

半﹌身腐败 2024-07-20 10:59:49

参考?

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;
};

References?

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;
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文