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

Зачем он это делает? Я же расставил в реализации уточнение из какого поля брать?


Ответы (0 шт):