Pra*_*ian 12
是的,C++ 11和C++ 14都不允许使用匿名结构.这个答案包含一些理由,为什么会这样.您需要命名结构,并且也无法在匿名联合中定义它们.
§9.5/ 5 [class.union]
...匿名联合的成员规范只应定义非静态数据成员.[ 注意:无法在匿名联合中声明嵌套类型,匿名联合和函数.- 尾注 ]
因此,将结构定义移到联合之外.
template <typename some_type>
struct vec
{
struct xyz { some_type x, y, z; };
struct rgb { some_type r, g, b; };
union {
xyz a;
rgb b;
some_type elements[3];
};
};
Run Code Online (Sandbox Code Playgroud)
现在,我们需要some_type为标准布局,因为这使得匿名工会的所有成员布局兼容.以下是标准布局类型的要求.这些在标准的第9/7节中描述.
然后,从§9.2[class.mem]
16如果 两个标准布局结构(第9节)类型具有相同数量的非静态数据成员且相应的非静态数据成员(按声明顺序)具有布局兼容类型(3.9),则它们是布局兼容的.
18 如果标准布局联合包含两个或多个共享公共初始序列的标准布局结构,并且如果标准布局联合对象当前包含这些标准布局结构中的一个,则允许检查共同的初始部分任何一位.如果相应的成员具有布局兼容类型并且两个成员都不是位字段,或者两者都是具有相同宽度的位字段(对于一个或多个初始成员的序列),则两个标准布局结构共享共同的初始序列.
对于数组成员,来自§3.9/ 9 [basic.types]
...标量类型,标准布局类类型(第9节),这些类型的数组和这些类型的cv限定版本(3.9.3)统称为标准布局类型.
要确保这some_type是标准布局,请在定义中添加以下内容vec
static_assert(std::is_standard_layout<some_type>::value, "not standard layout");
Run Code Online (Sandbox Code Playgroud)
std::is_standard_layout在type_traits标题中定义.现在联盟的所有3个成员都是标准布局,两个结构和数组是布局兼容的,因此3个联合成员共享一个共同的初始序列,这允许您编写然后检查(读取)属于该公共的任何成员初始序列(在你的情况下整个事情).
C++ 11/14允许使用匿名联合.在Bjarne Stroustrup的C++ 11 FAQ中查看它们的用法示例
关于匿名结构,请参阅为什么C++ 11不支持匿名结构,而C11呢?而为什么C++禁止匿名结构和联合的?
虽然大多数编译器都支持匿名结构,但如果您希望代码符合标准,则必须编写如下代码:
template <typename some_type>
struct vec
{
union {
struct { some_type x, y, z; } s1;
struct { some_type r, g, b; } s2;
some_type elements[3];
};
};
Run Code Online (Sandbox Code Playgroud)
我认为其他答案有点错过了问题的重点:
我希望以与此相同的方式访问成员.
换句话说,真正的问题是"我怎么定义一个类型,vec在符合标准的方式,从而给出一个对象u那种类型的,u.x,u.r,和u.elements[0]都指的是同样的事情?"
好吧,如果你坚持这种语法......那么显而易见的答案就是:引用.
所以:
template <typename some_type>
struct vec
{
vec() = default;
vec(const vec& other) : elements{ other.elements[0], other.elements[1], other.elements[2] } {}
vec & operator=(const vec &other) {
elements[0] = other.elements[0];
elements[1] = other.elements[1];
elements[2] = other.elements[2];
return *this;
}
some_type elements[3];
some_type &x = elements[0], &y = elements[1], &z = elements[2];
some_type &r = elements[0], &g = elements[1], &b = elements[2];
};
Run Code Online (Sandbox Code Playgroud)
这种方法的第一个问题是你需要6个参考成员的额外空间 - 这对于这样一个小结构来说相当昂贵.
这种方法的第二个问题是给定的const vec<double> v;,v.x仍然是类型的double &,所以你可以写v.x = 20;,并把它编译没有警告或错误-只得到未定义的行为.很糟糕.
因此,在替代方案中,您可以考虑使用访问器功能:
template <typename some_type>
struct vec
{
some_type elements[3];
some_type &x() { return elements[0]; }
const some_type &x() const { return elements[0]; }
some_type &y() { return elements[1]; }
const some_type &y() const { return elements[1]; }
some_type &z() { return elements[2]; }
const some_type &z() const { return elements[2]; }
some_type &r() { return elements[0]; }
const some_type &r() const { return elements[0]; }
some_type &g() { return elements[1]; }
const some_type &g() const { return elements[1]; }
some_type &b() { return elements[2]; }
const some_type &b() const { return elements[2]; }
};
Run Code Online (Sandbox Code Playgroud)
您必须编写u.x()等等而不是u.x,但节省的空间相当大,您还可以依赖编译器生成的特殊成员函数,如果some_type是(它可以进行一些优化),它可以轻易复制,它是一个聚合,因此可以使用聚合初始化语法,它也是const-correct.
演示.请注意,sizeof(vec<double>)第一个版本为72,第二个版本仅为24.