为何成员列表初始化的窄转换仅触发警告而非编译错误?
问题
已知使用花括号初始化列表的统一初始化不允许窄转换(例如double→int),但成员初始化列表却允许此类转换。示例代码如下:
#include <iostream> using namespace std; struct Mem1 { Mem1() { cout << "Mem1 - default" << endl; } Mem1(int i) { cout << "Mem1 - single arg" << endl; } }; struct Mem2 { Mem2() { cout << "Mem2 - default" << endl; } Mem2(int i) { cout << "Mem2 - single arg" << endl; } }; class A { Mem1 m1 {5.0}; // OK, 编译器警告 Mem2 m2 {2.0}; // OK, 编译器警告 public: A() { cout << "A - default" << endl; } A(int i) : m1{1.0}, m2{2.0} { cout << "A - single arg" << endl; } // OK }; int main() { A a0; // OK cout << "-------------" << endl; A a1 {2}; // OK //A a2 {2.0}; // Error - 窄转换 }
程序运行输出结果:
Mem1 - single arg Mem2 - single arg A - default ------------- Mem1 - single arg Mem2 - single arg A - single arg
使用MSVC 14.16编译时,m1、m2的初始化会触发警告C4244:'argument': conversion from 'double' to 'int', possible loss of data,但不会引发编译错误;而A a2 {2.0}则会直接报错。请问这是编译器bug,还是我遗漏了相关规则?
解答
这不是编译器bug,而是C++标准对不同场景下花括号初始化的窄转换检查规则存在差异:
- 直接对象的统一初始化(如
A a2 {2.0}):这类场景属于列表初始化的直接初始化范畴,C++标准明确要求禁止窄转换,编译器必须将其视为错误(默认严格遵循标准)。这里A a2 {2.0}试图用double类型的值初始化A的int参数构造函数,触发了窄转换检查,因此直接报错。 - 类成员的花括号初始化(如
Mem1 m1 {5.0}、成员初始化列表中的m1{1.0}):这类场景本质是通过花括号列表调用成员的构造函数,属于复制列表初始化或直接初始化成员构造函数的调用流程。此时标准并未强制要求将窄转换视为错误,而是允许编译器以警告形式提示风险。MSVC的行为完全符合标准:它检测到了窄转换的潜在数据丢失风险,发出警告但不阻止编译。
总结来说,标准对不同位置的花括号初始化设置了不同的窄转换检查严格度:全局/局部对象的统一初始化要求严格禁止窄转换,而类成员的花括号初始化则允许编译器仅以警告形式提示,而非强制报错。
内容的提问来源于stack exchange,提问作者ssh
相关产品推荐
相关产品推荐

