You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++花括号初始化私有构造对象未报编译错误的原因

复现代码

class DoorKeeper;

struct Door {
  class Key {
    Key() = default;
    // ~Key() = default; // [1]
    friend DoorKeeper;
  };
  void open(Key) {}
};

int main(int, char **) {
  Door().open(Door::Key()); // 始终报构造函数私有错误
  Door().open(Door::Key{}); // 仅取消注释[1]或新增成员时报错
}
行为差异原因

两种写法的行为不一致,是C++聚合初始化规则、特殊成员函数访问权限规则共同作用的结果,两种初始化语法走的是完全不同的执行路径:

1. 两种初始化的执行路径本质不同

  • 圆括号写法Door::Key()属于值初始化:对于类类型,值初始化流程会直接查找并调用类的默认构造函数,这一步必然会检查构造函数的访问权限。代码中Key的默认构造函数声明在私有区域,main函数不是Key的友元,无权限访问私有构造函数,因此这行代码永远会触发构造函数私有的编译错误。
  • 花括号写法Door::Key{}在Key满足聚合类条件时,会走聚合初始化路径:聚合初始化是逐成员初始化对象的成员,整个流程不会调用类的构造函数,因此哪怕构造函数是私有的,只要流程中没有用到该构造函数,就不会触发权限检查。

2. 默认状态下花括号初始化可通过编译的原因

在注释掉[1]行析构函数、未新增类成员的状态下,Key完全符合C11~C17标准中聚合类的判定要求:

  • 没有用户提供的构造函数:代码中Key() = default;是在首次声明时要求编译器生成默认构造,不属于用户手动提供实现的构造函数,不违反聚合类要求
  • 没有私有/保护的非静态数据成员:Key是空类,不存在非静态数据成员
  • 无基类、无虚函数
    此时Door::Key{}执行聚合初始化时,因为类没有需要初始化的非静态成员,整个流程完全不涉及对私有默认构造函数的调用,不会触发构造函数权限报错。同时该状态下Key的析构函数是编译器隐式生成的,隐式生成的特殊成员函数默认是public访问权限,临时对象销毁时调用public析构函数完全合法,因此整行代码可以正常通过编译。

3. 取消注释析构函数、新增成员时花括号初始化报错的原因

两种报错场景的触发逻辑不同:

  • 取消注释[1]行~Key() = default;时:class定义内的成员默认访问权限为private,该析构函数是显式声明在私有区域的私有成员,main函数不是Key的友元,无权限访问私有析构函数,而临时对象生命周期结束时必须调用析构函数,因此会触发析构函数私有的编译错误。此外C++20标准调整了聚合类判定规则,要求聚合类不能有用户声明的构造函数,此时Key因显式声明了默认构造函数不再属于聚合类,花括号初始化会转而尝试调用私有默认构造函数,同样会触发编译错误。
  • 为Key类新增成员时:如果新增的非静态成员写在类的默认私有区域(新增成员默认落在class开头的private段),会导致Key出现私有非静态数据成员,不再满足聚合类要求,花括号初始化会转而尝试调用私有默认构造函数,触发构造函数权限报错。如果新增的是public成员,Key仍然属于聚合类,花括号初始化不会触发构造函数权限检查。

内容的提问来源于stack exchange,提问作者OznOg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 23:51:30