标记为explicit的已删除构造函数是否有实际作用?
给已删除的构造函数添加
explicit修饰符是否有效果? 有细微但确实存在的效果,核心差异体现在重载决议的候选范围和编译器错误提示上:
1. 不带explicit的已删除构造函数
编译器会将其纳入隐式转换的候选构造函数列表。不管是显式调用(如MyClass(1))还是隐式转换(如MyClass a = 1;),只要触发了这个构造函数的调用,都会直接抛出「调用已删除函数」的错误。
比如这段代码:
class MyClass { public: MyClass(int) = delete; }; int main() { MyClass a = 1; // 错误:调用已删除的构造函数 MyClass::MyClass(int) MyClass b(2); // 错误:同样触发已删除函数调用 }
2. 带explicit的已删除构造函数
这个构造函数不会参与隐式转换场景的重载决议。也就是说:
- 显式调用(如
MyClass(1))依然会触发「调用已删除函数」的错误; - 隐式转换场景(如
MyClass a = 1;)下,编译器根本不会考虑这个构造函数,如果没有其他可行的转换路径,会抛出「无法从'int'转换到'MyClass'」的错误。
举个更明显的对比例子:
class MyClass { public: explicit MyClass(int) = delete; }; void func(MyClass) {} int main() { func(1); // 错误:无法将参数 1 从“int”转换为“MyClass” MyClass a(1); // 错误:调用已删除的构造函数 MyClass::MyClass(int) }
如果存在其他重载构造函数,这种差异还会影响最终的重载决议结果。比如:
class MyClass { public: MyClass(double) {} // 不带explicit的已删除构造函数 MyClass(int) = delete; }; void func(MyClass) {} void test() { func(1); // 错误:调用已删除的MyClass(int),因为int直接匹配int参数,不会转double }
而改成explicit MyClass(int) = delete;后:
class MyClass { public: MyClass(double) {} explicit MyClass(int) = delete; }; void func(MyClass) {} void test() { func(1); // 正常编译:int隐式转double,调用MyClass(double) }
总结
虽然两种写法最终都禁止了通过int直接构造MyClass的行为,但explicit修饰符会改变该构造函数在重载决议中的参与范围,进而影响错误提示和某些场景下的重载结果。
内容的提问来源于stack exchange,提问作者einpoklum
相关产品推荐
相关产品推荐

