C++. Ромбовидное наследование
Есть вот такие классы:
struct Employee {
protected:
std::string full_name_tilda;
public:
std::string full_name;
int base_salary_usd_per_year;
explicit Employee(std::string name, int salary);
[[nodiscard]] virtual int salary_usd_per_year() const;
static std::unique_ptr<Employee> read_from(std::istream & in);
virtual void print(std::ostream & out) const;
virtual ~Employee();
};
struct Developer : Employee {
std::string github_account;
explicit Developer(std::string name, int salary, std::string git);
[[nodiscard]] int salary_usd_per_year() const override;
static std::unique_ptr<Developer> read_from(std::istream & in);
void print(std::ostream & out) const override;
};
struct Manager : Employee {
std::string project_name;
explicit Manager(std::string name, int salary, std::string proj);
[[nodiscard]] int salary_usd_per_year() const override;
static std::unique_ptr<Manager> read_from(std::istream & in);
void print(std::ostream & out) const override;
};
struct LeadDeveloper : Developer, Manager {
explicit LeadDeveloper(std::string name, int salary, std::string git, std::string proj);
[[nodiscard]] int salary_usd_per_year() const override;
static std::unique_ptr<LeadDeveloper> read_from(std::istream & in);
void print(std::ostream & out) const override;
};
Вот реализации:
void Employee::print(std::ostream &out) const {
out << "Employee " << full_name_tilda << " " << base_salary_usd_per_year;
}
void Developer::print(std::ostream &out) const {
out << "Developer " << full_name_tilda << " " << base_salary_usd_per_year << " " << github_account;
}
void Manager::print(std::ostream &out) const {
out << "Manager " << full_name_tilda << " " << base_salary_usd_per_year << " " << project_name;
}
void LeadDeveloper::print(std::ostream &out) const {
out << "LeadDeveloper " << Developer::full_name_tilda << " " << Developer::base_salary_usd_per_year << " " << github_account << " " << project_name;
}
std::ostream & operator<<(std::ostream & out, const Employee & employ) {
employ.print(out);
return out;
}
std::ostream & operator<<(std::ostream & out, const Developer & dev) {
dev.print(out);
return out;
}
std::ostream & operator<<(std::ostream & out, const Manager & man) {
man.print(out);
return out;
}
std::ostream & operator<<(std::ostream & out, const LeadDeveloper & lead) {
lead.print(out);
return out;
}
Два уровня функций нужно только чтобы избежать использования friend (вдруг это надо для исправления ромбика, если нет лучше убрать).
При таком обращении снаружи:
std::string to_string(const employees::Employee &e) {
std::stringstream s;
static_cast<std::ostream &>(s) << e;
return s.str();
}
int main() {
employees::LeadDeveloper ld("Ivan Ivanov", 100'000, "ivanivanov",
"proj");
to_string(ld);
}
ld (который аргумент функции) говорит Ambiguous conversion from derived class 'const employees::LeadDeveloper' to base class 'const employees::Employee': struct employees::LeadDeveloper -> struct employees::Developer -> struct employees::Employee struct employees::LeadDeveloper -> struct employees::Manager -> struct employees::Employee
Зачем он это делает? Я же расставил в реализации уточнение из какого поля брать?