C++03迁移C++11时初始化列表触发explicit构造函数告警问题
C03迁移C11时
= {}初始化触发explicit构造告警的解决方法 问题复现信息
编译输出
使用GCC 9.4.0分别以C03、C11标准编译测试代码,C03下无任何报错告警,C11下输出告警如下:
% g++ -std=c++03 t.cxx % g++ -std=c++11 t.cxx t.cxx: In function ‘int main()’: t.cxx:8:21: warning: converting to ‘A’ from initializer list would use explicit constructor ‘A::A(int)’ 8 | int main() { B b = {}; } | ^ t.cxx:8:21: note: in C++11 and above a default constructor can be explicit
测试代码
struct A { explicit A(int i = 42) {} }; struct B { A a; }; int main() { B b = {}; return 0; }
编译器版本
% g++ --version g++ (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
告警根因
- C++03规则下,
explicit关键字仅会阻止构造函数被用于隐式单参数转换。A::A(int i=42)是带默认参数的普通构造函数,B b = {}属于聚合拷贝初始化,成员a未指定初始化子句时值初始化调用A的默认构造,不受explicit限制,因此写法合法。 - C11规则变更:*不需要传入实参即可调用的构造函数会被识别为默认构造函数,且C11允许默认构造函数被声明为
explicit*。拷贝初始化语境(即=语法的初始化)下,不允许隐式调用explicit构造函数,因此B b = {}触发告警。更高版本的GCC(12+)会直接将该场景判定为编译错误。
等价初始化实现方案
以下写法均和C++03下B b = {}的值初始化/零初始化效果完全一致,可根据代码风格选择:
- 最推荐:改用直接列表初始化
直接初始化语境允许调用explicit构造函数,语法最简洁,语义无任何变化:int main() { B b{}; // 聚合直接初始化,所有成员按规则值初始化,完全等价原C++03写法 return 0; } - 保留类赋值书写风格
显式构造临时对象后拷贝/移动初始化目标变量,语义等价:int main() { B b = B{}; return 0; } - 修改类型A的定义拆分默认构造
如果可以修改A的结构,可将默认构造单独实现为非explicit版本,从根源上避免规则冲突,C++11可直接用委托构造简化实现:
修改后原struct A { explicit A(int i) {} A() : A(42) {} // 非explicit默认构造,委托调用带参构造 };B b = {}写法在C++11下可正常编译,无任何告警。
内容的提问来源于stack exchange,提问作者malat
相关产品推荐
相关产品推荐

