explicit仅对单参数构造函数生效,阻止编译器隐式调用该构造函数完成类型转换,但不影响显式构造、static_cast或转换函数;移动构造函数也可加explicit以防意外隐式移动。
explicit 关键字只对单参数构造函数有效
它不是给所有转换加锁的万能开关,只作用于能被编译器用来做隐式类型转换的构造函数——也就是恰好接受
一个参数
(或多个参数但其余都有默认值)的构造函数。如果构造函数带两个及以上必须显式传入的参数,
根本不生效,也无需加。
常见错误现象:
意外触发了
构造,而你其实只想允许
这种写法。
使用场景:封装字符串、智能指针、数值包装类(如
、
)时,防止字面量或基础类型“悄悄”变成对象
参数差异:
禁止
;但
和
依然合法
性能影响:无。它只改变编译期行为,不生成额外代码
explicit 不阻止 static_cast 或直接构造调用
的目标是拦住「编译器自动插入的转换」,而不是禁止所有转换。只要人明确写了,它就放行。
常见错误现象:以为加了
就彻底“封死”了转换,结果发现
还是能过,或者
被误认为是
隐式转换
。
立即学习
“
C++免费学习笔记(深入)
”;
、
等显式转型始终绕过
限制
直接构造语法(
)也不是隐式转换,而是显式创建,不受影响
函数参数如果是
类型,传入
会触发隐式转换——这正是
要拦住的典型场景
用户定义的类型转换函数(operator T())不能加 explicit
只有构造函数能用
,反过来的转换——比如
自动转成
——靠的是
这类转换函数。这类函数本身不支持
修饰(C++11 起才支持
,且仅限
)。
C函数速查手册(CHM版)
C函数速查手册(CHM版)
下载
常见错误现象:加了
构造函数,却忘了类里还有
,导致
意外触发转换,甚至引发二义性。
若需控制向
的转换,可用
防止
这类误用
其他类型转换函数(
、
)无法显式标记,只能靠设计规避:比如改用命名函数
C++20 起仍不支持
,别试
移动构造函数和移动赋值也值得加 explicit
虽然移动操作通常不用于隐式转换,但某些模板上下文(尤其是完美转发或 ADL 查找中)可能意外触发移动构造,尤其当类型可被 move-only 且构造函数形参是右值引用时。
常见错误现象:在
中,若移动构造函数非
,配合某些编译器或标准库实现,可能引发非预期的临时对象构造链。
移动构造函数加
是保守做法,尤其当你不希望它参与任何隐式上下文(如
推导为
)
注意:拷贝构造函数默认就是隐式的,移动构造函数默认也是隐式的,二者行为一致,都可加
兼容性影响:极少。主流标准库容器(如
、
)内部使用直接初始化,不依赖隐式移动
最容易被忽略的是:explicit 只管“一条路”,而 C++ 的类型转换路径有好几条——构造函数、转换函数、模板推导、重载解析。光堵住构造函数,不代表万事大吉;得看清楚你真正想防的是哪一步转换,在哪个上下文中发生。
explicitMyString s = "hello";MyString(const char*)MyString s("hello");SecondsPathexplicit MyString(const char*)MyString s = "abc";MyString s("abc");func(MyString("abc"));explicitexplicitstatic_cast("hello") func(MyString("world"))static_castconst_castexplicitMyString("abc")MyString"hello"explicitexplicitMyStringconst char*operator const char*()explicitexplicit operator bool()boolexplicitoperator int()if (myIntObj) { ... }boolexplicit operator bool() constint x = myObj;operator double()operator std::string()to_string()explicit operator int()std::vector v = { std::move(x), std::move(y) }; explicitexplicitauto t = std::move(some_obj);MyTypeexplicitvectorunique_ptr