c++中explicit (以Sophus为例)

264 阅读2分钟

作者: 边城量子 ( shihezichen@live.cn )

背景

在阅读 Sophus 库代码时, 发现它也大量使用这个关键字, 举例如下:

common.hpp:

/// Nullopt type of lightweight optional class.
struct nullopt_t {
  explicit constexpr nullopt_t() {}
};

sim3.hpp 中类 Sim3的构造函数代码片段 :

  /// Constructor from 4x4 matrix
  ///
  /// Precondition: Top-left 3x3 matrix needs to be "scaled-orthogonal" with
  ///               positive determinant. The last row must be ``(0, 0, 0, 1)``.
  ///
  SOPHUS_FUNC explicit Sim3(Matrix<Scalar, 4, 4> const& T)
      : rxso3_(T.template topLeftCorner<3, 3>()),
        translation_(T.template block<3, 1>(0, 3)) {}

介绍

在构造函数前面增加 explicit 关键字修饰, 作用是什么呢? explicitimplicit 这两种方式有什么区别呢?

C++中用 explicit 来修饰一个类的构造函数时,它表明这个构造函数是显式的。不加 explicit 或加 implicit 修饰时,则表明时隐式的。

1. 一个例子

class ExString 
{
public:
    ExString(int size):_size(size){
        _pstr = malloc(size + 1); // 为_pstr分为内存,大小为size
        memset(_pstr, 0, size + 1);  
    }
    EXString(const char* p){
        int size = strlen(p);
        _pstr = malloc(size + 1);
        strncpy(_pstr, p, size);
        _size = strlen(_pstr);
    }
    // 以下为析构函数, 省略
    
private:
    char* _pstr;
    int _size;
    
};

以上的类定义, 遇到如下的构造语句时,表现如下:

ExString str1(24);    // OK, 分配了24字节内存
ExString str2 = 10;   // OK, 分配了10字节内存,下面会展开分析
ExString str3;        // NOK, 因为没有默认构造函数
ExString str4("aaa"); // OK, 调用的是EXString(const char* p)
ExString str5 = 'c';  // OK,把'c'看为ascii码转为整数,然后调用ExString(int size)

上述代码中, ExString str2 = 10; 是怎么OK的呢?

2. 分析

在C++中, 当构造函数只有一个参数时,则编译时就会生成一个缺省的转换操作,来把参数转换为本类对象,所以ExString str2 = 10; 等同于如下:

ExString temp(10);      // 数据类型转换
ExString str2 = temp;  

这种隐式的数据类型转换很多时候并不是预期的,如何组织编译器做这种转换呢? 答案就是用 explicit 关键字修饰, 如下:

class ExString 
{
public: 
    explicit ExString(int size):_size(size) {
       // 代码同上,省略...
    }
    // 代码同上,省略...
};

这个时候执行构造语句,很多需要转换数据类型的语句就不能成功了:

ExString str2 = 10;   // NOK, explicit关键字存在, 不能隐式把10转为ExString了
ExString str5 = 'c';  // NOK, 同上

3.结论

  • explicit 就是防止类构造函数的隐式数据转换
  • explicit 可应用的场合:
    • 当类构造函数只有一个参数时
    • 当类构造函数有两个及以上参数时,若第2个参数和其后参数都有默认值时
  • 原则: 当只有一个参数的类构造函数情况下, 请显式增加 explicit 清晰表达期望的效果.