如何正确运用 explicit 关键字以避免类间的隐式转换?
- 内容介绍
- 文章标签
- 相关推荐
本文共计761个文字,预计阅读时间需要4分钟。
当你写如下的代码:
- 只对**单参数构造函数**(或多个参数但其余都有默认值)起作用
- 不加
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),它就不管了。
本文共计761个文字,预计阅读时间需要4分钟。
当你写如下的代码:
- 只对**单参数构造函数**(或多个参数但其余都有默认值)起作用
- 不加
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),它就不管了。

