explicit 是修饰构造函数和转换运算符的关键字,用于禁止隐式类型转换;它适用于单参数构造函数(含默认参数后仅一个非默认参数)及 c++11 起的转换运算符(如 explicit operator bool()),但不适用于多参数构造函数或拷贝/移动构造函数。

什么是 explicit 关键字?
它不是“运算符”,而是作用于构造函数和转换运算符的修饰符,用来禁止编译器做隐式类型转换。C++ 默认允许单参数构造函数或 operator T() 被悄悄调用,这常导致意料之外的类型转换,比如传错参数、意外创建临时对象、重载决议出人意料。
加 explicit 后,这些转换必须显式写出,比如用 static_cast 或直接构造调用。
- 只对单参数构造函数(含默认参数后只剩一个非默认参数)有效
- 也适用于 C++11 起的转换运算符:
explicit operator bool() const - 不适用于多参数构造函数(它们本来就不会隐式调用)
explicit 构造函数怎么写?常见错误在哪?
没加 explicit 的构造函数是隐患源头。例如:class String { public: String(const char*); }; 这会让 func(String) 接收 "hello" 时自动构造临时 String,而你可能根本没打算支持这种用法。
正确写法是:
立即学习“C++免费学习笔记(深入)”;
class String {
public:
explicit String(const char* s);
};这样 func("hello") 就会编译失败,必须写成 func(String("hello")) 或 func(static_cast<string>("hello"))</string>。
- 错误:给有多个非默认参数的构造函数加
explicit—— 没意义,也不报错,但纯属干扰阅读 - 错误:忘了给带默认参数的构造函数加
explicit,比如explicit Widget(int x = 0, int y = 0)实际仍可能隐式转换(仅用一个参数调用时) - 注意:
explicit不影响拷贝/移动构造函数,它们本就不参与隐式转换
如何安全定义类型转换?为什么 explicit operator bool() 很关键?
自定义类型的布尔上下文转换(比如 if (obj) {...})最容易出问题。老式写法 operator bool() const 会导致 obj == true、obj + 1 这类非法操作也能通过编译——因为 bool 会被提升为 int。
C++11 推荐写法:
class FileHandle {
public:
explicit operator bool() const { return fd_ != -1; }
private:
int fd_;
};这样 if (fh) {...} 合法,但 fh + 42 或 fh == nullptr 直接编译失败。
- 不加
explicit的operator bool是历史遗留坑,很多旧代码因此出现静默错误 - 其他转换运算符(如
operator int())也建议加explicit,除非你明确需要隐式转整数 - 注意:
explicit operator T()要求 C++11 或更高标准,老项目需确认编译器支持
什么时候不该加 explicit?性能或接口设计上要注意什么?
加 explicit 是安全优先的选择,但极少数场景要权衡可读性或兼容性。比如数值类型包装器(struct Meter { double val; explicit Meter(double v) : val(v) {} }),强制写 Meter(1.5) 比 1.5 更啰嗦,且无实质风险。
更现实的取舍点在于模板和泛型代码:
- STL 容器(如
std::vector)的迭代器构造函数通常不explicit,因为算法依赖隐式转换(如std::sort(first, last)中的first可能是int*) - 如果你的类要被泛型算法使用,加
explicit前先测试是否破坏了std::begin、std::distance等推导 - 性能上无影响——
explicit是编译期约束,不生成额外指令
真正容易被忽略的是:转换运算符的 explicit 必须和所有重载一起考虑。比如同时提供 explicit operator bool() 和 operator int(),后者若没加 explicit,就又打开了隐式转换后门。









