在GCC 4.6.1上,当我声明一个具有默认构造函数的我自己的类型的实例时,如果我实例化该类型的对象并用大括号(如Foo my_foo {};)初始化它,那么POD成员如果没有声明其他构造函数,那么该类只会初始化为零。如果除了默认的构造函数之外没有其他构造函数,它们将像预期的那样归零。
但是,在GCC 4.7.3中,零初始化以任何一种方式发生,这是我预期的行为。
这有什么区别?这是编译器错误吗?这两个GCC版本都支持C ++ 11标准的默认构造函数。
没有必要坚持旧的GCC版本,但我想了解这里发生了什么。
注意:我违反了主要的ctor,op =。并且复制ctor只是为了保持类型可用于可变参数函数(clang要求将类分类为POD,尽管gcc让我使用具有可变函数的类型,即使用户定义的主ctor。奖励积分,如果你能告诉我我为什么。)
这是一个示例程序,包括底部的一些输出(来自使用两个GCC版本编译的二进制文件):
#include <cstdio>
// pod and pod_wctor are identical except that pod_wctor defines another ctor
struct pod {
pod( void ) = default;
pod( const pod& other ) = default;
pod& operator=( const pod& other ) = default;
int x,y,z;
};
struct pod_wctor {
pod_wctor( void ) = default;
pod_wctor( const int setx, const int sety, const int setz ) : x(setx), y(sety), z(setz) { }
pod_wctor( const pod_wctor& other ) = default;
pod_wctor& operator=( const pod_wctor& other ) = default;
int x,y,z;
};
int main ( void ) {
printf("the following shuold be uninitialized:\n");
pod pee;
printf( " %i,%i,%i\n", pee.x, pee.y, pee.z);
pod_wctor podtor;
printf( " %i,%i,%i\n", podtor.x, podtor.y, podtor.z);
printf("the following shuold be initialized to 0,0,0:\n");
pod peenit{};
printf( " %i,%i,%i\n", peenit.x, peenit.y, peenit.z );
pod_wctor podtornit{};
printf( " %i,%i,%i\n", podtornit.x, podtornit.y, podtornit.z );
return 0;
}
// compiled with: g++ m.cpp -std=gnu++0x
// g++ (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1 (i386)
/****************** output *******************
the following shuold be uninitialized:
10381592,134513249,134520820
134513969,134513504,0
the following shuold be initialized to 0,0,0:
0,0,0
7367877,134513945,8724468
*********************************************/
// compiled with: g++ m.cpp -std=gnu++0x
// gcc version 4.7.3 (Ubuntu/Linaro 4.7.3-2ubuntu4) (i386)
/****************** output *******************
the following shuold be uninitialized:
-1218358300,-1217268232,134520832
134514450,1,-1079827548
the following shuold be initialized to 0,0,0:
0,0,0
0,0,0
*********************************************/
答案 0 :(得分:2)
通过将构造函数pod_wctor( const int setx, const int sety, const int setz ) : x(setx), y(sety), z(setz) { }
添加到您的类中,它会失去聚合状态:[dcl.init.aggregate] / 1
聚合是一个数组或类(第9条),没有用户提供的构造函数
它仍然是一个POD,因为普通类只需要没有非平凡的默认 ctors:[class] / 6
普通类是一个具有默认构造函数(12.1)的类,没有非平凡的默认构造函数, 并且可以轻易复制。
这里有趣的一点是,对于聚合,list-initialization pod peenit{};
执行聚合初始化:
类型
T
的对象或引用的列表初始化定义如下:
- 如果
T
是聚合,则执行聚合初始化(8.5.1)。 [...]- 否则,如果初始化列表没有元素且
T
是具有默认构造函数的类类型,则该对象是值初始化的。
(注意:这是修订后的订单.AFAIK,在标准本身,这两个点的顺序是相反的,这必然是一个缺陷,因为每个聚合都有一个默认的ctor - 隐式声明和定义的一个。 )
聚合初始化导致int
成员的值初始化:[dcl.init.aggr] / 7
如果列表中的 initializer-clauses 少于聚合中的成员,那么未显式初始化的每个成员都应从空的初始化列表中初始化
和[dcl.init.list] / 3“否则,如果初始化列表没有元素,则对象进行值初始化”
但是,对于非聚合pod_wctor
,list-initialization pod_wctor podtornit{}
直接执行值初始化,它调用默认的ctor。 [class.ctor] / 6指定:
隐式定义的默认构造函数执行该类的初始化集合,该初始化集合将由该用户编写的默认构造函数执行,该类没有 ctor-initializer (12.6.2)和空复合语句。
并在[class.base.init] / 8中找到:
在非委托构造函数中,如果给定的非静态数据成员或基类未由 mem-initializer-id 指定(包括没有 mem的情况) -initializer-list 因为构造函数没有 ctor-initializer )并且实体不是抽象类的虚拟基类(10.4),那么
- [...]
- 否则,实体默认初始化(8.5)。
默认的ctor 本身不保证成员归零,因为它只对成员进行默认初始化。
默认值和值初始化之间的区别:[dcl.init]
[7] default-initialize
T
类型的对象意味着:
- 如果
T
是(可能是 cv - 限定的)类类型,T
的默认构造函数被称为[...]- [...]
- 否则,不执行初始化。
[...]
[8] value-initialize
T
类型的对象意味着:
- 如果
T
是一个(可能是 cv - 限定的)类类型,没有默认构造函数或者是用户提供或删除的默认构造函数,那么该对象是默认初始化的;- 如果
T
是一个(可能是 cv - 限定的)非联合类类型而没有用户提供或删除的默认构造函数,那么该对象将被初始化为零,如果{ {1}}有一个非平凡的默认构造函数,默认初始化;- [...]
- 否则,该对象为零初始化。
(我承认这让我很困惑,我不得不修改我的答案。)
T
有一个默认构造函数,不是用户提供的。因此,对于列表初始化pod_wctor
,值初始化的第二个子弹适用。对象pod_wctor podtornit{}
本身是零初始化,这导致其成员的零初始化。只有然后才会默认初始化,并且将调用默认的ctor。后者什么都不做,但前者保证成员将被归零。