Почему нельзя: если мы объявим ящик бананов ящиком фруктов и положим туда яблоко, он перестанет быть ящиком бананов.
#include <iostream>
class Vocal { // интерфейс
public:
virtual void shout() = 0;
virtual ~Vocal() = default;
};
class Dog : public Vocal {
public:
virtual void shout() override { std::cout << "Woof!" << std::endl; }
};
class Unrelated {};
// ВАРИАНТ 1. Метод выкручивания рук.
void shoutAll_v1(Vocal** x, int n)
{
for (int i = 0; i < n; ++i)
x[i]->shout();
}
template <class T, int N>
inline void shoutAll_v1(T* (&x)[N]) {
// Тупая проверка концепции, ждём Си++20
static_assert (std::is_base_of<Vocal, T>(), "Need array of Vocal");
T** xx = x;
shoutAll_v1(reinterpret_cast<Vocal**>(xx), N);
}
// ВАРИАНТ 2. Виртуальный полиморфизм.
class Choir { // интерфейс
public:
virtual int size() const = 0;
virtual Vocal& at(size_t i) const = 0;
virtual ~Choir() = default;
};
void shoutAll_v2(const Choir& x)
{
int sz = x.size();
for (int i = 0; i <sz; ++i)
x.at(i).shout();
}
template <class T>
class VocalArray : public Choir {
public:
template <int N>
VocalArray(T* (&x)[N]) : fData(x), fSize(N) {}
int size() const override { return fSize; }
Vocal& at(size_t i) const override { return *fData[i]; }
private:
T** const fData;
int fSize;
};
int main()
{
Dog* sons[3];
for(auto& x : sons) {
x = new Dog;
}
//Unrelated* unrel[3];
std::cout << "V1" << std::endl;
shoutAll_v1(sons);
//shoutAll_v1(unrel); не компилируется
std::cout << "V2" << std::endl;
shoutAll_v2(VocalArray<Dog>(sons));
//shoutAll_v2(VocalArray<Unrelated>(unrel)); не компилируется
return 0;
}