C++中同一变量的多个名称

6

在C++中,是否可以不使用预处理器就能够用不同的名称引用同一个变量?

为了达到与这个伪代码相同的效果:

struct vec3f {
    float[3] values;
};

struct color : public vec3f {
    #define r values[0]
    #define g values[1]
    #define b values[2]
};

color c;
c.r = 0.5f;

以下代码在语义上是正确的,但在结构体中分配了三个引用的空间:
struct color : public vec3f {
    float& r;
    float& g;
    float& b;
    color() : r(values[0]), g(values[1]), b(values[2]) { }
};

有没有一种方法可以在不增加结构体大小的情况下进行编译时名称替换?
4个回答

10

这个怎么样?

struct vec3f {
    float[3] values;
};

struct color : public vec3f
{
    float& r() { return values[0]; }
    float& g() { return values[1]; }
    float& b() { return values[2]; }
    const float& r() const { return values[0]; }
    const float& g() const { return values[1]; }
    const float& b() const { return values[2]; }
};

我曾尝试避免使用括号,但这绝对是正确的方法。 - wjd

1

恰好,我几年前第一次看到了一个非常巧妙的解决方法

这个想法是你按顺序给类命名变量,然后还有一个static const成员数组指向成员类型。重载operator[]以查找适当的成员指针,使用它从this选择成员并返回引用。

这个方法可行是因为指向成员的指针不是普通指针;它们比普通指针更神奇。(这就是为什么你可以创建未绑定的成员函数指针,以及为什么它们不能在期望普通函数指针的地方使用)。

这也意味着你不必使用任何强制转换技巧,依赖于任何种类的对齐、非可移植的匿名联合行为或内存布局保证,而且你仍然可以通过命名字段而不是访问器函数来引用结构的组件。


虽然这是一个很好的技巧,但我不明白当color应该是vec3f时它如何有所帮助。 - Mooing Duck
@MooingDuck 如果我们要像将颜色视为向量一样奇怪地做某些事情,我几乎看不出继承的方向有什么关系,甚至是否存在继承...因此,您可以使用重载的operator[]r/g/b成员创建一个color,然后将vec3f定义为它。 - Karl Knechtel

1

我不确定你是否要在这种情况下使用继承。你可能最好使用一个普通的union类型:

typedef float vec3f[3];
union color {
   vec3f values;
   struct {
      float r;
      float g;
      float b;
   };
};

color c;
c.values[0] = 10;
assert( c.r == 10 );

我认为不幸的是,那必须是 c.values.values[0] - Mooing Duck
1
@MooingDuck: ?? vec3f 类型是一个 typedef,而不是原始的 struct,代码本身是正确的。c.values.values[0] 将会导致编译错误。 - David Rodríguez - dribeas
哇,没有注意到typedef。 - Mooing Duck
很遗憾,这是未定义的行为,因为数组不参与“共同初始子序列”规则,允许联合体通过不同于写入的成员进行读取。 - Ben Voigt

1

备选方案1

当你想要一个变量别名时,你总是创建一个临时变量。使用优秀的优化器,你几乎看不到任何性能差异。

struct vec3f
{
    float values[3];
};

struct tempvec
{
    float &r;
    float &g;
    float &b;

    tempvec( vec3f& bar )
        :r(bar.values[0]) 
        , g(bar.values[1]) 
        , b(bar.values[2]){}
};

int main() 
{
    vec3f temp;
    temp.values[0] = 2.40f;

    //when you want to alias values[0] as r do this
    tempvec(temp).r = 42;
    tempvec(temp).g = 42;

    return 0;    
}

备选方案2

如果您可以验证在您的平台和操作系统上,vec3fvec3c 的内存布局相同,考虑到填充/对齐等因素... 您可以执行以下操作:

struct vec3f
{
    float values[3];
};

struct vec3c
{
    float r,g,b;
};

int main() 
{
    vec3f temp;
    temp.values[0] = 2.40f;

    vec3c* alias  = reinterpret_cast<vec3c*>(&temp);

    alias->r = 4.2f;
    alias->g = 4.2f;
    alias->b = 4.2f;

    return 0;    
}

你的第二个选择让我发现了类型转换运算符,所以 color 可以写成 struct color { float r, g, b; operator vec3f&() { return *((vec3f*)this); }; - wjd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接