为什么
struct wrapper
{
explicit wrapper(void *);
wrapper() = default;
int v;
};
int main() { return wrapper().v; } // You should run this in Debug mode
返回0xCCCCCCCC
,而
struct wrapper { wrapper() = default; int v; };
int main() { return wrapper().v; }
和
struct wrapper { int v; };
int main() { return wrapper().v; }
都返回0
?
答案 0 :(得分:4)
在值初始化期间,如果T
是没有用户提供或删除的默认构造函数的类类型,则该对象零初始化 (第8.5节/ 8.2)。 wrapper
确实如此。
您的第一个示例与零初始化的第三种情况匹配(§8.5/ 6.1,强调我的)
- 如果
T
是标量类型(3.9),则将对象初始化为通过转换整数文字获得的值0
(零)到T
;- 如果
T
是(可能是cv限定的)非联合类类型,则每个非静态数据成员和每个基类子对象都是零初始化的,并且填充初始化为零位;- 如果
T
是(可能是cv限定的)联合类型,则对象的第一个非静态命名数据成员将进行零初始化,并将填充初始化为零位;- 如果T是数组类型,则每个元素都是零初始化
- 如果T是引用类型,则不执行初始化
所以在你的第一个例子中,v
应该是零初始化的。这看起来像个错误。
在你的第二个和第三个例子中,你不再拥有一个用户提供的构造函数,但你确实有一个非用户提供或删除的默认构造函数,所以你的例子仍然属于第三种情况,用于零初始化,这是对每个非静态数据成员进行零初始化。 VS在那里是正确的。
答案 1 :(得分:4)
这似乎是MSVC中的一个错误。在所有三种情况下wrapper
都没有用户提供的默认构造函数,因此使用wrapper()
进行初始化调用:
(来自n3690的所有引文)
(8.5 / 11)一个对象,其初始化器是一组空的括号,即(),应进行值初始化。
(感谢dyp),这将导致int v
初始化然后引用我们的规则:
(8.5 / 8)如果T是没有用户提供或删除的默认构造函数的(可能是cv限定的)类类型,则该对象被零初始化并且检查默认初始化的语义约束。
零初始化规则状态:
(8.5 / 6)如果T是(可能是cv限定的)非联合类类型,则每个非静态数据成员和每个基类子对象都是零初始化的,并且填充初始化为零位
int v
作为wrapper
的数据成员,根据以下内容初始化为零:
(8.5 / 6)如果T是标量类型(3.9),则将对象初始化为通过将整数0(零)转换为T
获得的值
这不是您观察到的行为。