限制类编译器调用构造函数的隐式转换(单一入参构造函数)
我们知道C++是一个类型严格的语言,比如下面一个函数
void test_int_proxy(const int_proxy& v) {
printf("%d", v.value());
}
调用者对其传参也应该是一个int_proxy的对象,但是实际情况并非如此。那该如何表述,我个人觉得应该是:编译器对其传参应该是一个int_proxy对象。这两种表述的区别就是“调用者”和“编译器”的区别。我们来看一个实际例子,我们先假定int_proxy类这么定义:
class int_proxy {
public:
int_proxy(int n) : _m(n) {};
public:
int value() const {
return _m;
}
private:
int _m;
};
该类非常简单,它有一个带参数的构造函数,并使用参数列表形式初始化类的成员变量。
一般情况下我们都会这么调用test_int_proxy方法:
test_int_proxy(int_proxy(100));
这种写法我想没人会有异议,但是如果出现下面这种写法,就可能让人感觉不可接受了:
test_int_proxy(100);
然而,这种写法对上述类的定义来说是合法的!其效果和使用int_proxy控制住是一样的。这是为什么呢?这便是类构造函数的隐式转换技术。C++编译器认为test_int_proxy方法传入的应该是一个const类型的int_proxy对象,然而如果它发现参数不是该对象时,就会使用该类中可以使用该参数进行构造对象的方法构造出一个临时的对象。我们例子中传参100是个int型数据,而int_proxy正好有一个携带int参数的构造函数。稍微总结下类构造函数隐式转换的必要条件:
找不到传参类型严格对应的函数
找到传参类型严格匹配的类的构造函数
因为隐式转换构造出的是临时对象,所以不可修改,故触发隐式转换的函数的传参类型必须要使用const修饰
但是个人觉得这种“奇巧淫技”还是不用为好。比如我们代码中还有如下函数:
void test_int_proxy(const int& v) {
printf("%d", v + 100);
}
那么C++编译器会针对传100的调用上面这个过程。这样一个函数调用有两个匹配的调用方法就会产生不确定性——这儿指的不确定性并非是指编译器调用哪个方法的不确定性,而是指维护这段代码的人对上述代码做调整时容易忽略一些问题而导致的“人祸”。
再比如,我们在代码中加入下面类和方法
class int_proxy_2 {
public:
int_proxy_2(int n) : _m(n) {};
public:
int value() const {
return _m + 100;
}
private:
int _m;
};
void test_int_proxy(const int_proxy_2& v) {
printf("%d", v.value());
}
那么编译器不能确定隐式转换是要转换哪个类,更不知道是调用哪个test_int_proxy方法了。
限制类构造函数的隐式转换的方法也很简单,就是给对应的构造函数加上explict关键字
class int_proxy {
public:
explicit int_proxy(int n) : _m(n) {};
这样通过隐式转换而构造临时对象的图谋将会被察觉并禁止。
---------------------