explicit用于单参数构造函数(或带默认值的多参数构造函数),禁止编译器执行隐式类型转换,仅允许显式调用如String s(10)或String s{10},而String s = 10则编译失败。

explicit 用在构造函数上,阻止编译器悄悄调用单参数构造
当你写 class String { String(int n); },然后又写了 void print(String s);,接着调用 print(10); —— 这时编译器会自动把 10 转成 String 对象,哪怕你根本没想让它这么干。加 explicit 就是告诉编译器:“别自作主张,必须显式构造”。
- 只对**单参数构造函数**(或多个参数但其余都有默认值)起作用
- 不加
explicit:可能引发意外转换,比如if (s == "hello")触发临时String("hello")构造,性能差还难调试 - 加了之后,
String s = 10;编译失败,但String s(10);或String s{10};依然合法 - C++11 起支持
explicit用于转换运算符,比如explicit operator bool() const;,防止if (obj & 2)这类误用
哪些构造函数该加 explicit?看是否“语义上不是类型转换”
不是所有单参构造都要加 explicit,关键看它是不是在定义“怎么从别的类型变成我”。比如:
-
std::vector<int>(size_t n)</int>—— 是构造,不是“把 size_t 转成 vector”,加explicit合理(C++11 已加) -
std::string(const char*)—— 是“把 C 字符串转成 string”,属于自然转换,不加explicit(加了反而破坏直觉) - 自定义包装类如
SafeInt(int x)—— 如果你希望func(SafeInt{42})显式、func(42)报错,就加explicit
explicit 和初始化列表 {} 的关系容易被忽略
加了 explicit 的构造函数,仍能用 {} 直接初始化,但不能用于拷贝初始化(= 形式)。这个细节常导致困惑:
-
String s1{10};✅ 允许(直接初始化) -
String s2 = 10;❌ 编译失败(拷贝初始化,触发隐式转换) -
void f(String); f({10});✅ 允许(直接传参,不经过隐式转换) -
f(10);❌ 失败(必须显式写出构造意图)
模板构造函数加 explicit 需谨慎
模板构造函数加 explicit 时,效果取决于实例化后的参数个数。比如:
立即学习“C++免费学习笔记(深入)”;
template<typename T>
class Wrapper {
public:
explicit Wrapper(T value) : val(value) {}
private:
T val;
};
这时 Wrapper<int> w = 42; 失败,但 Wrapper<std::string> w = "hi"; 可能成功 —— 因为 std::string 的 const char* 构造本身不是 explicit,所以整条转换链未必被拦住。更安全的做法是:只对明确知道“不该隐式转换”的具体类型加 explicit,或用 std::enable_if 约束。
最常被漏掉的是:explicit 不影响赋值运算符重载,也不影响 move/copy 构造;它只管“从其他类型构造我”这一条路。一旦绕过构造(比如用 operator= 或 emplace),它就不管了。










