当存在其他构造函数时,"ctor() = default"为什么会更改行为?

Why does "ctor() = default" change behavior when other constructors are present?

本文关键字:为什么 default 构造函数 其他 存在 ctor      更新时间:2023-10-16

为什么

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

值初始化期间,如果T是没有用户提供或删除的默认构造函数的类类型,则该对象初始化为零 (§8.5/8.2)。wrapper确实如此。

您的第一个示例与零初始化的第三种情况匹配(§8.5/6.1,强调我的)

— 如果T是标量类型 (3.9),则对象初始化为通过转换整数文本获得的值 0(零)到T;

如果T是(可能符合 CV 条件的)非联合类类型,则每个非静态数据成员和每个基类子对象初始化为零,填充初始化为零位;

— 如果 T 是(可能符合 CV 条件的)联合类型,则对象的第一个非静态命名数据成员初始化为零,填充初始化为零位;

— 如果 T 是数组类型,则每个元素都是零初始化的

— 如果 T 是引用类型,则不执行初始化

因此,在您的第一个示例中,v 应该是零初始化的。这看起来像一个错误。

在第二个和第三个示例中,您不再有用户提供的构造函数,但您确实有一个不是用户提供或删除的默认构造函数,因此您的示例仍属于零初始化的第三种情况,即零初始化每个非静态数据成员。VS 在那里是正确的。

这似乎是 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 获得的值

这不是你观察到的行为。