错误C2664:无法将实参从初始化列表转换为std:初始化列表



我有一个函数read(),它读取文件并将信息存储到映射中。然而,每当函数调用map.insert()时,它就会给我一个错误。

EmployeeVolunteer是两个只有少量变量的海关类。例如,如果我调用

ifstream fin;
std::map<std::string, Employee*> employees;
fin.open("Employee.txt");
read<Employee, EMPLOYEE_SIZE>(fin, employees);

给出如下错误:

std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::insert(std::initializer_list<std::pair<const std::string,Employee *>>)': cannot convert argument 1 from 'initializer list' to 'std::initializer_list<std::pair<const std::string,Employee *>>'`. 

这是函数(EmployeeVolunteer都有相同的基类:)

template <typename T, int T_LINE_SIZE>
inline void read(std::ifstream& in, std::map<std::string, T*>& input) {
std::string name = "";
std::string ID="";
std::string line;
double salary;
while (getline(in,line)) {
if (typeid(T) == typeid(Volunteer)) {
name = line.substr(0, 19);
ID = line.substr(20, T_LINE_SIZE);
input.insert({name,new Volunteer(ID,name)}); //error happens here
}else if (typeid(T) == typeid(Employee)) {
name = line.substr(0, 19);
ID = line.substr(20, 29);
salary = std::stod(line.substr(30, T_LINE_SIZE));
input.insert({ name,new Employee(ID, name, salary) }); //error happens here
}
}
}

我在这里做错了什么?

试试下面的代码:

template <typename T, int T_LINE_SIZE>
inline void read(std::ifstream& in, std::map<std::string, T*>& input) {
std::string name = "";
std::string ID="";
std::string line;
double salary;
while (getline(in,line)) {
if constexpr (std::is_same<T, Volunteer>::value) {
name = line.substr(0, 19);
ID = line.substr(20, T_LINE_SIZE);
input.insert({name,new Volunteer(ID,name)}); 
}else if constexpr (std::is_same<T, Employee>::value) {
name = line.substr(0, 19);
ID = line.substr(20, 29);
salary = std::stod(line.substr(30, T_LINE_SIZE));
input.insert({ name,new Employee(ID, name, salary) }); 
}
}
}

原因是你正在使用模板。根据调用read()函数时T的类型,在调用insert()方法的一个if条件范围内T的类型不正确。这是因为需要在编译时针对不同类型检查代码的所有可能结果。但是当你使用if constexpr时,那部分代码在编译时被忽略,所以在编译代码时不会看到不正确的代码部分,你的代码可以正确编译。

@Afshin已经展示了一种方法,你可以做到这一点,但我认为这可能不是最好的方法,至少作为一种规则。

通过稍微不同地分布逻辑,您最终可以得到我认为相当简单的代码。

我将通过重载operator>>来读取需要存储的每个类的一个对象:

struct Volunteer { 
std::string name;
std::string ID;
friend std::istream &operator>>(std::istream &is, Volunteer &v) { 
std::string line;
std::getline(is, line);
v.name = line.substr(0, 19);
v.ID = line.substr(20); // captures the rest of the line
return is;
}
};
struct Employee { 
std::string name;
std::string ID;
double salary;
friend std::istream &operator>>(std::istream &is, Employee &e) {
std::string line;
std::getline(is, line);
e.name = line.substr(0, 19);
e.ID = line.substr(20, 29);
e.salary = std::stod(line.substr(30);
return is;
}
};

我可能在VolunteerEmployee中省略了其他(可能重要的)内容——我只包含了足够从文件中读取一个,并存储我们读取的数据的内容。

有了这些,从文件中读取它们变得相当简单:

template <class T>
inline void read(std::istream &is, std::map<std::string, T *> &input) {
T t;
while (is >> t)
input.emplace(t.name, new T(t));
}

在本设计中,Employees和Volunteers都知道如何从文件中读取它们自己的数据,并且读取记录的代码不需要知道如何读取每个记录的细节。这使得代码更具可扩展性。例如,让我们考虑一下为Intern添加另一个类时会发生什么。在您的原始代码中(由Afshin修复),您将向if/else if链添加第三条腿:

else if constexpr (std::is_same<T, Intern>::value) {
// code to read and insert data for an Intern here
}

相反,在我提倡的设计中,所有处理Intern的代码都应该在Intern类本身中(或者至少与之关联):

struct Intern {
std::string name;
std::string ID;
bool paid;
double salary;
friend std::istream &operator>>(std::istream &is, Intern &i) {
// code to read data for an Intern here
}
};

…因此,在这种情况下,从文件读取数据的代码不需要任何更改来处理Interns而不是Employees或Volunteers的记录文件。

最新更新