包含常量成员的POD结构

qbt*_*937 11 c++ const language-lawyer c++03

使用此代码:

struct A
{
    int i;
    const int b;
};

// The union is to verify that A is a type that can be used in a union.
union U
{
    A a;
    int b;
};

int main()
{
    U a = {1, 1};
    U b = {2, 1};
}
Run Code Online (Sandbox Code Playgroud)

g ++版本4.8.3抱怨错误:

a.cpp:9:4: error: member ‘A U::a’ with copy assignment operator not allowed in union
  A a;
    ^
a.cpp:9:4: note: unrestricted unions only available with -std=c++11 or -std=gnu++11
Run Code Online (Sandbox Code Playgroud)

但是clang 3.5.0编译这段代码没有错误.哪一个是正确的?这是编译器错误吗?

我试图解决这个问题:

从C++ 03标准第9.5节第1段:

在联合中,最多一个数据成员可以在任何时间处于活动状态,也就是说,最多一个数据成员的值可以随时存储在并集中.[注意:为了简化联合的使用,我们做了一个特别的保证:如果一个POD-union包含几个共享一个公共初始序列的POD结构(9.2),并且这个POD-union类型的一个对象包含一个在POD结构中,允许检查任何POD结构成员的共同初始序列; 见9.2.] union的大小足以包含其最大的数据成员.每个数据成员都被分配,就好像它是结构的唯一成员一样.联合可以具有成员函数(包括构造函数和析构函数),但不具有虚函数(10.3).工会不得有基类.联合不得用作基类.具有非平凡构造函数(12.1),非平凡复制构造函数(12.8),非平凡析构函数(12.4)或非平凡复制赋值运算符(13.5.3,12.8)的类的对象不能是一个联盟的成员,也不是一系列这样的对象.如果联合包含静态数据成员或引用类型的成员,则该程序格式错误.

从C++ 03标准部分12.8第10和11段:

如果类定义未显式声明复制赋值运算符,则会隐式声明一个.X& X::operator=(const X&)如果X的每个直接基类B都有一个复制赋值运算符,其参数类型为const B&,const volatile B&或B,并且对于所有非静态数据成员,则类X的隐式声明的复制赋值运算符将具有该形式.类型为M(或其数组)的X,每个这样的类类型都有一个复制赋值运算符,其参数类型为const M&,const volatile M&or M.

Otherwise, the implicitly declared copy assignment operator will have the form X& X::operator=(X&) ...

如果隐式声明类X并且类X没有虚函数(10.3)且没有虚基类(10.1),并且X的每个直接基类都有一个普通的复制赋值运算符,则类X的复制赋值运算符是微不足道的. X的所有非静态数据成员都是类类型(或其数组),每个这样的类类型都有一个普通的复制赋值运算符 ; 否则复制赋值运算符是非常重要的.

我不确定哪个编译器是正确的,因为我不知道常量成员是否有一个简单的复制赋值运算符.

编辑: 编译命令是:

clang++ a.cpp -o a
g++ a.cpp -o a
Run Code Online (Sandbox Code Playgroud)

Edit2: 为了表明g ++不抱怨A :: b,const但A没有构造函数,我也试过这个程序:

struct A
{
    int i;
    const int b;
};

int main()
{
    A a = {1, 1};
}
Run Code Online (Sandbox Code Playgroud)

这在g ++和clang ++上编译时没有错误:

g++ b.cpp -o b
clang++ b.cpp -o b
Run Code Online (Sandbox Code Playgroud)

Col*_*mbo 4

正如您正确指出的那样,复制赋值运算符是隐式声明的并且是微不足道的。默认构造函数也是如此,它也很简单并且是隐式声明的。

请注意,尽管这两个成员函数都没有隐式定义- 仅在使用它们时才会发生,[class.ctor]/7:

类的隐式声明的默认构造函数在用于创建其类类型的对象时会隐式定义(1.8)。

..这里显然不是这种情况。
这是关键的区别,也是 @dasblinkenlight 的引用与此事无关的原因:默认构造函数从未定义,因此缺少mem-initializer-id 的段落不适用。

const那么成员和赋值运算符是如何联系的呢?这里:

当为其类类型的对象分配其类类型的值或从其类类型派生的类类型的值时,隐式定义了隐式声明的复制赋值运算符。 如果隐式定义复制 赋值运算符的类具有以下情况,则程序是格式错误的:

  • const类型的非静态数据成员,或 [..]

因此,如果使用复制赋值运算符,程序将是格式错误的。但事实并非如此。所有特殊成员函数都是单独声明的,并且对const非静态数据成员的性质的任何限制仅适用于隐式定义的特殊成员函数。

举个例子,以

struct A
{
    int i;
    const int b;
};

int main()
{
    A a = {1, 1};
}
Run Code Online (Sandbox Code Playgroud)

在 GCC 下编译得很好。您的程序也应该是格式良好的,因为 可以满足对工会成员特殊成员函数的琐碎性的所有要求A