Bootstrap

C++的explicit关键字

exlicit 的用处

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

  • 构造函数只有一个参数

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

#include 
using namespace std;
class Foo {
public:
  Foo(int x){ cout << "Foo(int)"<

输出:

Foo(int)
Foo(int)
Foo(int)
Foo(int)
Foo(double, double)

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

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

class Foo {
public:
  explicit Foo(int x){ cout << "Foo(int)"<

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};) (参考)

More

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

#include 

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 的使用条件如下:

以 《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, 但是写错了,但是仍然可以通过编译
 //...
}

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

参考