作者: 边城量子 ( 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 关键字修饰, 作用是什么呢? explicit 和 implicit 这两种方式有什么区别呢?
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清晰表达期望的效果.