explicit 关键字用于禁止编译器对单参数构造函数(含默认参数可单参调用)进行隐式类型转换,只允许显式调用;如 String s = "hello"; 或传参时自动转临时对象均被禁止,避免歧义。

explicit 关键字用于修饰构造函数(C++11 起也支持转换运算符),它的核心作用是禁止编译器进行隐式类型转换,只允许显式调用构造函数完成对象初始化。
什么时候需要 explicit?
当类有一个单参数构造函数(或有默认参数导致实际可单参数调用)时,编译器可能悄悄把它当作“类型转换函数”来用——这往往不是你想要的,容易引发歧义或意外行为。
- 比如
String s = "hello";看似赋值,实则调用了String(const char*)构造函数 - 再比如函数接收
String,你传入"world",编译器自动转成临时对象 - 这些“自动发生”的转换,就是隐式转换,explicit 就是用来关掉它的
explicit 的典型写法和效果
在构造函数声明前加 explicit:
class String {
public:
explicit String(const char* s); // ✅ 禁止隐式转换
// String s = "abc"; // ❌ 编译错误
// func(someString); // 若 func 接收 String,传 "abc" 会报错
// String s("abc"); // ✅ 显式调用,合法
// String s = String("abc"); // ✅ 显式构造再拷贝,合法(但通常直接用前者)
};C++11 起:explicit 也能用于转换运算符
防止类到其他类型的隐式转换:
class Number {
int val_;
public:
explicit operator int() const { return val_; } // ✅ 禁止自动转 int
// int x = n; // ❌ 错误:不能隐式转换
// int x = static_cast(n); // ✅ 必须显式转换
// printf("%d", n); // ❌ 即使 printf 需要 int,也不自动转
}; 哪些情况可以不加 explicit?
不是所有单参构造函数都要加 explicit。常见例外:
-
智能指针类(如
std::unique_ptr):设计上就鼓励从裸指针安全构建,隐式转换在此语境下是预期行为(raw_ptr) -
数值包装类(如
std::complex):数学语义清晰,隐式构造更自然(3.0) - 你明确希望支持类似
func(MyClass(42))或MyClass x = 42;的简洁语法,且已评估过歧义风险
不复杂但容易忽略:只要构造函数形参能由一个参数推导出来(含默认参数),又没加 explicit,它就可能成为隐式转换的入口。加 explicit 是防御性编程的低成本高回报实践。











