写点什么

C++ 的 explicit 关键字

作者:行者孙
  • 2021 年 12 月 14 日
  • 本文字数:1383 字

    阅读完需:约 5 分钟

exlicit 的用处

explicit 关键字告诉编译器,拒绝隐式转换。主要修饰以下情况的构造函数:


  • 构造函数只有一个参数

  • 构造函数有多个参数,但是除了第一个参数外都具有默认值


#include <iostream>using namespace std;class Foo {public:  Foo(int x){ cout << "Foo(int)"<<endl;}  Foo(double x, double y = 0){  cout << "Foo(double, double)" <<endl;}  };
int main(){ Foo a = 42; //OK: 发生了隐式转换 Foo b(42); //OK Foo c = Foo(42); //OK Foo d = (Foo)42; //OK Foo e = 42.0; //OK: 发生了隐式转换 return 0;}
复制代码


输出:


Foo(int)Foo(int)Foo(int)Foo(int)Foo(double, double)
复制代码


Foo a = 42 这句发生了隐式类型转换,效果就是调用了Foo(int)并且把 42 作为参数。隐式类型转换有一些便利之处,也会带来一些隐患。( ref《more effective C++》)


如果使用了 explicit,将会禁止把 42 隐式转换为 class Foo 的实例 a,这句将会在编译时报错。


class Foo {public:  explicit Foo(int x){ cout << "Foo(int)"<<endl;}  explicit Foo(double x, double y = 0){  cout << "Foo(double, double)" <<endl;}};
int main(){ Foo a = 42; //Compile-time error: can't convert 42 to an object of type Foo return 0;}
复制代码


main.cpp:23:13: error: conversion from ‘int’ to non-scalar type ‘Foo’ requested


  • (更新)C++11 中 explicit 关键字还能作用于 C++11 的列表初始化语法


class Foo {  explicit Foo(int x, double y);  ...};
复制代码


此时下面的代码是不允许的:


Func({42, 3.14});  // Error
复制代码


Note: 似乎不必要限制这种转换, 接受一个 std::initializer_list 作为参数的构造函数也应当省略 explicit, 以便支持拷贝初始化 (例如 MyType m = {1, 2};) (参考Google Style Guide)

More

explicit 可以混用,《C++ F&Q 10.22》有个例子如下, 这里本意是允许 double 隐式转换为 Foo,但是不允许 bool 转换为 Foo。


#include <iostream>
class Foo {public: Foo(double x) { std::cout << "Foo(double)\n"; } explicit Foo(bool x) { std::cout << "Foo(bool)\n"; }};
void yourCode(){ Foo a = true; //OK: implicitly promotes true to (double)1.0, then calls Foo::Foo(double)}
复制代码


但是发现可以通过编译,并且打印如下


Foo(double)


这里的 true 先被编译器解释为 (double)true, 即 1.0 ,然后通过 Foo(double) 完成了隐式类型转换。这个与设计的本意不符合,可能会带来困惑或者 bug。

何时使用

expilicit 的使用条件如下:


  1. 对于单自变量构造函数:包括单参数构造函数和除了第一个参数外都有默认值的构造函数适用。

  2. 如果不想让它被隐式转换。 根据《More Effective C++》中条款 5:对“类型转换函数保持警觉”,在编程实践中如非必要, 禁止允许隐式类型转换。 因此通常若非设计如此,建议使用 explicit。


以 《More Effective C++》的例子来看,


class Array{  Array(int size);  bool operator==(const Array& lhs, const Array& rhs);};
...
Array a(10);Array b(10);

if(a == b[i]){ // bug!: 本意是 a==b, 但是写错了,但是仍然可以通过编译 //...}
复制代码


因此, 如非必要,拒绝隐式类型转换。

参考

  1. http://www.cs.technion.ac.il/users/yechiel/c++-faq/explicit-ctors.html

  2. Scott Meyers,《More Effective C++》

发布于: 1 小时前阅读数: 7
用户头像

行者孙

关注

Nothing replaces hard work 2018.09.17 加入

充满好奇心,终身学习者。 博客:https://01io.tech

评论

发布
暂无评论
C++的explicit关键字