我有一个C ++类,其中的构造函数针对不同的数据类型进行了重载。一个说明问题的简化示例是:
#include <iostream>
#include <vector>
#include <string>
class c {
public:
c(const std::string& n, int v) :
name(n),
int_value(v)
{
std::cout << "Running <int> constructor" << std::endl;
}
c(const std::string& n, double v, const std::vector<double>& extra_args) :
name(n),
double_value(v)
args(extra_args)
{
std::cout << "Running <double> constructor" << std::endl;
}
private:
std::string name;
int int_value;
double double_value;
std::vector<double> args;
};
int main(int argc, char **argv) {
c i("name", int());
// This line should in my opinion not compile at all; but
// it ends up calling the (std::string&, int) constructor.
c d("name", double());
}
如您所见,构造函数使用double
参数期望值和其他std::vector<double>
参数。我的期望是根本不编译c::c("name", double())
调用-我指望通过编译器来帮助我进行重构,但是相反,调用了采用整数参数的构造函数,然后事情逐渐浮出水面。
因此,当我编译并运行示例程序时,输出为:
Running <int> constructor
Running <int> constructor
我根本没想到它会编译。
答案 0 :(得分:5)
对于重载解析,编译器首先必须检查重载是否可以接受调用中提供的参数数量。这是一个确定该函数调用表达式可行的过程。
第一个重载只能正好接受两个,而第二个重载只能正好接受三个。在两种情况下,立即取消第二次过载的资格。因此,重载集包含两个调用的单个成员。
现在,编译器必须查看它是否可以形成从每个参数到每种参数类型的转换序列。 "name"
文字通过构造函数转换为std::string
,而double
隐式转换为int
。因此,对于一组中的一个且唯一的过载,形成转换序列是成功的。这样,它就会被调用。
答案 1 :(得分:1)
double
可以隐式转换为int
,从而可以调用第一个构造函数。
如果您要确保在传递double
时不会有人调用它,可以显式删除它。
class c {
public:
c(const std::string& n, double d) = delete;
.
.
.
};
int main(){
c a{"A", 1); // ok, calls c(const std::string& n, int i);
//c b{"B", 1.0); Compile error! tries to call deleted function
// c(const std::string& n, double d)
c b{"C", static_cast<int>(1.0)}; //ok, calls c(const std::string& n, int i);
}