struct A{
A(){}
};
union C{
A a;
int b = 0;
};
int main(){
C c;
}在上面的代码中,GCC与宗族都抱怨联合C的默认构造函数被定义为已删除。
然而,有关规则规定:
在以下情况下,类X的默认构造函数被定义为已删除:
注意强调的措辞。在本例中,由于变体成员b具有默认成员初始化器,因此不应将默认的默认构造函数定义为“删除”。为什么这些编译器将这些代码报告为格式错误?
如果将C的定义更改为
union C{
A a{};
int b;
};然后所有的编译器都可以编译这段代码。该行为暗示,该规则实际上意味着:
X是一个具有一个具有非平凡的默认构造函数的变体成员的联合,并且没有为提供默认的成员初始化器,即变体成员。
这是编译器的错误还是该规则含糊的措辞?
发布于 2020-12-22 09:24:10
这在C++14和C++17之间进行了更改,通过CWG 2084添加了允许NSDMI在(任意)联合成员上恢复默认构造函数的语言。
不过,随附的CWG 2084示例与您的示例略有不同:
struct S {
S();
};
union U {
S s{};
} u;在这里,NSDMI位于非平凡的成员上,而C++17的措辞允许任何成员上的NSDMI恢复默认的构造函数。这是因为,正如博士所写的,
NSDMI基本上是mem初始化器的语法糖。
也就是说,int b = 0;上的NSDMI基本上相当于用mem初始化器和空体编写构造函数:
C() : b{/*but use copy-initialization*/ 0} {}顺便说一句,确保联盟中最多有一个变体成员有NSDMI的规则在class.union.anon的一个子子句中有些隐藏。
4-.联合的一个变体成员最多可以有一个默认的成员初始化器。
我的假设是,,因为gcc和Clang已经允许上面的(非平凡的工会成员的NSDMI ),他们没有意识到他们需要改变他们的实现以获得完全的C++17支持。
这是在名单上讨论性病-2016年讨论,有一个非常类似于您的例子:
struct S {
S();
};
union U {
S s;
int i = 1;
} u;其结论是,clang和gcc在拒绝上有缺陷,尽管当时有一个误导性的注解,结果是已修订。
对于Clang来说,bug是bug.cgi?id=39686,它将我们循环回隐式定义构造函数因变量成员而被删除,N 3690/N 4140 vs N 4659/N 4727。我找不到gcc相应的窃听器。
注意MSVC正确接受,并将c初始化为.b = 0,即每dcl.init.aggr正确
5-.如果聚合是一个联合并且初始化程序列表是空的,那么
发布于 2020-12-22 10:30:38
联盟是一件棘手的事情,因为所有成员共享相同的内存空间。我同意,规则的措辞不够清楚,因为它忽略了显而易见的内容:为一个联合的多个成员定义默认值是未定义的行为,或者应该导致编译器错误。
请考虑以下几点:
union U {
int a = 1;
int b = 0;
};
//...
U u; // what's the value of u.a ? what's the value of u.b ?
assert(u.a != u.b); // knowing that this assert should always fail. 这显然不应该编译。
此代码确实编译,因为A没有显式的默认构造函数。
struct A
{
int x;
};
union U
{
A a; // this is fine, since you did not explicitly defined a
// default constructor for A, the compiler can skip
// initializing a, even though A has an implicit default
// constructor
int b = 0;
};
U u; // note that this means that u.b is valid, while u.a has an
// undefined value. There is nothing that enforces that
// any value contained by a struct A has any meaning when its
// memory content is mapped to an int.
// consider this cast: int val = *reinterpret_cast<int*>(&u.a) 这段代码无法编译,因为A::x确实有一个显式默认值,这与U::b (双关意)的eplicit默认值发生了冲突。
struct A
{
int x = 1;
};
union U
{
A a;
int b = 0;
};
// Here the definition of U is equivalent to (on gcc and clang, but not for MSVC, for reasons only known to MS):
union U
{
A a = A{1};
int b = 0;
};
// which is ill-formed.由于大致相同的原因,这段代码在gcc上也不会编译,但是会在MSVC上工作(MSVC总是比gcc严格一点,所以也就不足为奇了):
struct A
{
A() {}
int x;
};
union U
{
A a;
int b = 0;
};
// Here the definition of U is equivalent to:
union U
{
A a = A{}; // gcc/clang only: you defined an explicit constructor, which MUST be called.
int b = 0;
};
// which is ill-formed.至于在哪里报告错误,无论是在声明点还是实例化点,这取决于编译器,gcc和msvc在初始化点报告错误,当您尝试实例化联合时,clang将报告错误。
注意,这是非常不可取的,有一个联盟的成员是不兼容的,或至少比特相关。这样做破坏了类型安全性,并且是对程序中bug的公开邀请。类型双关是可以的,但是对于其他用例,应该考虑使用std::variant<>。
https://stackoverflow.com/questions/65404305
复制相似问题