explicit关键字用于禁止单参数构造函数的隐式转换,触发场景包括函数传参、赋值初始化和返回值匹配;它仅作用于可单实参调用的构造函数,显式调用需用直接初始化或显式构造。
explicit 关键字用来禁止编译器执行单参数构造函数的隐式转换,这是防止意外类型转换最直接有效的手段。
当类有一个接受单个参数的构造函数(或多个参数但其余都有默认值),且该构造函数未加 explicit 时,编译器可能在不写明构造调用的情况下自动完成类型转换。
常见场景包括:
int)直接传给期望该类对象的函数= 进行初始化(非拷贝初始化,而是直接初始化的语法糖)例如:String s = "hello"; 若 String(const char*) 构造函数没加 explicit,这行就合法——但你未必想要这种“悄悄构造”。
explicit 修饰的是构造函数声明本身,不是调用方式。它只对恰好能通过一个实参调用的构造函数起作用(含多参数但其余均有默认值的情况)。
以下写法都受 explicit 约束:
explicit String(int n);explicit String(const char* s, bool copy = true);(因为可仅用一个参数调用)而这些不受影响:
String(int n, const char* s);(必须两个参数,无法隐式转换)explicit operator int() const;(C++11 起也支持 explicit 类型转换函数,但这是另一回事)加了 explicit 不代表不能用,只是拒绝“悄悄地”构造。你需要显式写出构造动作:
String 
s(42); 或 String s{"hello"};
foo(String(100)); 而不是 foo(100);
static_cast(不推荐,除非接口强制要求):foo(static_cast(100));
注意:String s = String(42); 是允许的(这是拷贝初始化,但右边是显式构造),而 String s = 42; 会被拒绝。
即使构造函数加了 explicit,某些模板上下文仍可能绕过限制,比如:
std::vector v(10, "abc"); —— 这里 "abc" 会尝试隐式转成 String,若构造函数是 explicit,编译失败std::make_unique(42) 是 OK 的(参数直接转发给构造函数),但 std::make_unique("abc") 同样依赖是否允许隐式转换更隐蔽的是聚合初始化与 explicit 无关,所以 struct S { explicit S(int); }; S s{42}; 合法,但 S s = {42}; 非法。
真正要注意的不是“要不要加 explicit”,而是“这个构造意图是否应该被用户无感知地触发”。多数情况下,单参数构造函数代表一种明确的、有代价的转换,显式比隐式更安全、更可维护。