GCC 4.6 和 4.7 之间的默认 CTOR 差异
defaulted ctor differences between gcc 4.6 and 4.7
在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,%in", pee.x, pee.y, pee.z);
pod_wctor podtor;
printf( " %i,%i,%in", podtor.x, podtor.y, podtor.z);
printf("the following shuold be initialized to 0,0,0:n");
pod peenit{};
printf( " %i,%i,%in", peenit.x, peenit.y, peenit.z );
pod_wctor podtornit{};
printf( " %i,%i,%in", 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
*********************************************/
通过将构造函数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),没有非平凡默认构造函数的类, 并且是微不足道的可复制的。
这里有趣的一点是,对于聚合,列表初始化pod peenit{};
执行聚合初始化:
类型为
T
的对象或引用的列表初始化定义如下:
- 如果
T
是聚合,则执行聚合初始化 (8.5.1)。[...]- 否则,如果初始值设定项列表没有元素,并且
T
是具有默认构造函数的类类型,则对象将进行值初始化。
(注:这是修订后的命令。AFAIK,在标准本身中,这两个点的顺序是颠倒的,这肯定是一个缺陷,因为每个聚合都有一个默认的 ctor - 隐式声明和定义的一个。
聚合初始化导致int
成员的值初始化:[dcl.init.aggr]/7
如果列表中的初始值设定项子句少于聚合中的成员数,则应从空的初始值设定项列表初始化未显式初始化的每个成员
和 [dcl.init.list]/3 "否则,如果初始值设定项列表没有元素,则对象是值初始化的"
但是,对于非聚合pod_wctor
,列表初始化pod_wctor podtornit{}
直接执行值初始化,这调用默认的 ctor。[class.ctor]/6 指定:
隐式定义的默认构造函数执行类的一组初始化,该初始化将由用户为该类编写的默认构造函数执行,没有 ctor-initializer (12.6.2) 和空复合语句。
在 [class.base.init]/8 中,我们发现:
在非委托构造函数中,如果给定的非静态数据成员或基类不是由 mem-initializer-id 指定的(包括没有 mem-initializer-list 的情况,因为构造函数没有 ctor 初始值设定项),并且该实体不是抽象类的虚拟基类 (10.4),则
- [...]
- 否则,实体默认初始化 (8.5)。
默认 ctor 本身不保证成员的清零,因为它只对成员进行默认初始化。
默认初始化和值初始化之间的区别:[dcl.init]
[7] 默认初始化类型
T
的对象意味着:
- 如果
T
是(可能符合 CV 条件的)类类型,则T
的默认构造函数称为 [...]- [...]
- 否则,不执行初始化。
[...]
[8] 对类型为
T
的对象进行值初始化意味着:
- 如果
T
是(可能符合 CV 条件的)类类型,没有默认构造函数或用户提供或删除的默认构造函数,则对象是默认初始化的;- 如果
T
是没有用户提供或删除的默认构造函数的(可能符合 CV 条件的)非联合类类型,则该对象为零初始化,如果T
具有非平凡的默认构造函数,则默认初始化;- [...]
- 否则,对象初始化为零。
(我承认这让我感到困惑,我不得不修改我的答案。
pod_wctor
有一个不是用户提供的默认构造函数。因此,对于列表初始化pod_wctor podtornit{}
,应用值初始化的第二个项目符号。对象podtornit
本身初始化为零,这会导致其成员初始化为零。只有这样,它才会被默认初始化,并且默认的 ctor 才会被调用。后者什么都不做,但前者保证成员将被归零。
- 如何创建一个CMake变量,除非显式重写,否则使用默认值
- 如何使用默认参数等选择模板专业化
- 具有默认模板参数的多态类的模板推导失败
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 当函数模板参数是具有默认参数的类模板时,函数模板参数的推导如何执行
- 初始化具有非默认构造函数的std::数组项的更好方法
- 何时提供默认参数作为模板参数
- 是默认情况下分配给char数组常量的值
- std::is_base_of表示ctor编译错误
- 具有默认值的引用获取函数
- 为什么 std::optional::value_or 没有默认 ctor 类型的专用化?
- 初始化在类类型 #define 中定义的非静态成员数组,不带默认 ctor
- C 11- ctor()=默认和空ctor(){}之间有什么区别
- 使用非默认 Ctor 初始化类类成员
- 为什么我不能对具有成员初始值设定项列表的默认 ctor 使用 =default。
- 如果明确默认的constexpr ctor允许非constexpr初始化
- 将没有默认 ctor 的对象插入到 std::map
- 为什么要删除基类的默认复制并移动ctor和赋值
- GCC 4.6 和 4.7 之间的默认 CTOR 差异
- 在类ctor中的默认nullptr指针给出了无法解析的外部符号c++