类类型value-initialization的标准条款解读与代码行为疑问
一、为什么示例代码输出0而非不确定值?
你对标准中“no default constructor”的理解有误。这里的“无默认构造函数”并非指用户未显式声明默认构造函数,而是指编译器无法隐式生成,且用户也未提供可访问的默认构造函数的情况。
你的示例代码里:
class S { long double d; friend void f(const S&); };
编译器会为S隐式生成一个平凡的默认构造函数(因为类没有用户声明的构造函数,也没有阻止隐式生成的因素,比如引用成员、未初始化的const成员等)。此时S属于标准条款(8.1.2)的范畴:类有隐式生成的、非用户提供的默认构造函数,因此值初始化会先执行零初始化,再检查默认初始化的约束——由于这个默认构造函数是平凡的,零初始化后就完成了初始化,所以成员d的值是0,最终输出0。
如果想让S符合(8.1.1)中“无默认构造函数”的情况,你可以给类添加一个用户声明的非默认构造函数,比如:
class S { long double d; friend void f(const S&); public: S(int) {} // 用户声明的非默认构造函数,编译器不再隐式生成默认构造函数 };
此时S没有任何默认构造函数,尝试S s{};会直接编译报错,因为默认初始化无法进行。
二、关于[dcl.init]/8.1.1条款的困惑澄清
你误解了条款中“no default constructor”的定义,同时需要明确:当类类型T确实没有可访问的默认构造函数,或者默认构造函数被删除时,T object{};是无法通过编译的,不存在这种情况下还能完成值初始化的合法场景。
条款(8.1.1)的实际含义是:当类T满足以下任一条件时,值初始化的行为等价于默认初始化:
- T不存在任何可访问的默认构造函数(包括用户未提供、编译器也无法隐式生成的情况);
- T有用户提供的默认构造函数;
- T的默认构造函数被删除。
但注意,第一种情况(无默认构造函数)下,默认初始化本身就是非法的,所以T object{};会编译失败——条款只是定义了如果这种初始化合法时的行为,但实际上这种场景下初始化根本无法进行。
比如,当类有一个被删除的默认构造函数时:
class S { public: S() = delete; };
尝试S s{};会编译报错,因为默认构造函数被删除,无法完成默认初始化,自然也无法完成值初始化。
内容的提问来源于stack exchange,提问作者mada

