带explicit默认构造函数时,空大括号拷贝列表初始化为何遭编译器拒绝?
列表初始化中explicit构造函数的编译器行为解析
一、单explicit默认构造函数的编译报错问题
先看这段代码:
struct A { explicit A() {} }; int main() { A a = {}; }
所有主流编译器都会拒绝编译这段代码,核心争议点在于A a = {};的初始化规则判定:
- 提问者认为根据标准[dcl.init.list]/3.5,该初始化属于值初始化,值初始化会直接调用默认构造函数,且默认初始化不排除
explicit构造函数,理应通过编译。 - 但编译器实际采用的逻辑是:
A a = {};属于拷贝列表初始化,需执行[over.match.list]的重载决议规则——在拷贝列表初始化中,即便选中explicit构造函数,也会因规则限制导致初始化格式错误,因此编译失败。
这里的标准措辞存在一定模糊性,但主流编译器均倾向于将空列表的=初始化判定为拷贝列表初始化,而非直接触发值初始化,因此拒绝编译。
二、添加模板默认构造函数后的行为差异
修改后的代码如下:
struct A { explicit A() {} template<typename = void> A() {} }; int main() { A a = {}; }
此时不同编译器出现行为分歧:
- GCC和Clang:仍拒绝编译,错误信息显示它们依然判定这是拷贝列表初始化,重载决议中优先选择了优先级更高的非模板
explicit默认构造函数,触发了拷贝列表初始化禁止选中explicit构造函数的规则。 - EDG和MSVC:接受编译,它们的处理逻辑是在拷贝列表初始化中直接排除
explicit构造函数,候选函数仅剩下非explicit的模板构造函数,因此选中该模板构造函数完成初始化。
这种差异源于编译器对“拷贝列表初始化中是否将explicit构造函数纳入候选集”的实现选择:部分编译器将其纳入候选但选中后报错,部分则直接排除explicit构造函数,仅考虑非explicit的候选。
内容的提问来源于stack exchange,提问作者user17732522
相关产品推荐
相关产品推荐

