Полимофизм для оператора "<<"

#include <iostream>

class A {};
class B : public A {};

std::ostream& operator<<(std::ostream& out, const A* a) {
    return out << "A";
}
std::ostream& operator<<(std::ostream& out, const B* B) {
    return out << "B";
}

int main() {
    A* arr[2];
    arr[0] = new A();
    arr[1] = new B();

    std::cout << arr[0] << arr[1];
}

Вот пример программы, которая выводит AA, как мне уточнить без явного каста к B(типо B*(arr[1])), что оператор << должен принимать B во втором случае?


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

Автор решения: Harry

А зачем? Может, лучше пусть ваш оператор << для A использует внутри виртуальную функцию? Тем более что в вашем варианте ничего виртуального нет, и в результате нет и полиморфизма.

Вот такое решение вас не устроит?

class A {
public:
    virtual string name() const { return "A"; }
};
class B : public A {
public:
    string name() const override { return "B"; }
};

std::ostream& operator<<(std::ostream& out, const A& a) {
    return out << a.name();
}

int main() {
    A* arr[2];
    arr[0] = new A();
    arr[1] = new B();

    std::cout << *arr[0] << *arr[1];
}
→ Ссылка
Автор решения: αλεχολυτ

Чтобы говорить о каком-то полиморфизме - нужны виртуальные функции. В вашем примере их вовсе нет. А есть всего лишь две перегруженные функции operator<<, отличающиеся типом последнего параметра. Какая из перегруженных функций будет вызвана определяется статическими типами переменных, передаваемых в функцию. Что arr[0], что arr[1] имеют статический тип A*, а значит будет вызвана первая перегрузка в обоих случаях.

std::cout << arr[0] << arr[1];
// Т.е. вот здесь   ^^   нет ничего такого, что сказало бы компилятору - бери динамический тип!

Кстати, замечу, что при перегрузке операторов обычно используются ссылки, а не указатели. Собственно, ссылки в языке и появились, чтобы можно было красиво сделать перегрузку операторов.

Другой момент в том, что operator<< по смыслу первым аргументом должен принимать поток вывода, а для виртуальных функций первым аргументом всегда будет объект this, для которого реализуется эта виртуальная диспетчеризация. Ну а вообще, можно вот так извратиться (не делайте так!):

#include <iostream>

struct A {
    virtual std::ostream& operator<< (std::ostream& o) { o << "A"; return o; }
};

struct B : A {
    std::ostream& operator<< (std::ostream& o) override { o << "B"; return o; }
};

int main()
{  
    A* arr[2] = {new A, new B};

    *arr[0] << std::cout;
    *arr[1] << std::cout;
}

Вывод:

AB

P.S. Адекватная реализация вашей задачи уже представлена в ответе Harry.

→ Ссылка