Ответ 1
Почему ваш код не работает:
Вызов виртуальной функции по значению не использует полиморфизм. Он вызывает функцию, которая определена для типа этого точного символа, как видно компилятору, а не типа времени выполнения. Когда вы вставляете подтипы в вектор базового типа, ваши значения будут преобразованы в базовый тип ( "тип slicing" ), который не является тем, что вы хотите. Вызывающие функции на них теперь вызовут функцию, определенную для базового типа, поскольку она не имеет такого типа.
Как это исправить?
Такую же проблему можно воспроизвести с помощью этого фрагмента кода:
templateInterface x = templateClass<int>(); // Type slicing takes place!
x.someFunction(); // -> templateInterface::someFunction() is called!
Полиморфизм работает только с указателем или ссылочным типом. Затем он будет использовать тип времени выполнения объекта за указателем/ссылкой, чтобы решить, какую реализацию вызывать (используя vtable).
Преобразующие указатели полностью "безопасны" в отношении разрезания типов. Ваши фактические значения вообще не будут преобразованы, и полиморфизм будет работать, как ожидалось.
Пример, аналогичный фрагменту кода выше:
templateInterface *x = new templateClass<int>(); // No type slicing takes place
x->someFunction(); // -> templateClass<int>::someFunction() is called!
delete x; // Don't forget to destroy your objects.
Что относительно векторов?
Итак, вы должны принять эти изменения в своем коде. Вы можете просто сохранить указатели на фактические типы в векторе, вместо того, чтобы напрямую хранить значения.
При работе с указателями вы также должны заботиться об удалении выделенных объектов. Для этого вы можете автоматически использовать интеллектуальные указатели, которые заботятся об удалении. unique_ptr
- один из таких интеллектуальных типов указателей. Он удаляет указателя, когда он выходит из сферы действия ( "уникальная собственность" - область действия владельца). Предполагая, что время жизни ваших объектов связано с областью, это то, что вы должны использовать:
std::vector<std::unique_ptr<templateInterface>> v;
templateClass<int> *i = new templateClass<int>(); // create new object
v.push_back(std::unique_ptr<templateInterface>(i)); // put it in the vector
v.emplace_back(new templateClass<int>()); // "direct" alternative
Затем вызовите виртуальную функцию на одном из этих элементов со следующим синтаксисом:
v[0]->someFunction();
Убедитесь, что все виртуальные функции должны быть переопределены подклассами. В противном случае их переопределенная версия не будет вызываться. Но поскольку вы уже ввели "интерфейс", я уверен, что вы работаете с абстрактными функциями.
Альтернативные подходы:
Альтернативные способы сделать то, что вы хотите, - использовать вариантный вектор в векторе. Существуют некоторые варианты вариантов вариантов, Boost.Variant, являющийся очень популярным. Этот подход особенно хорош, если у вас нет иерархии типов (например, когда вы храните примитивные типы). Затем вы использовали бы векторный тип типа std::vector<boost::variant<int, char, bool>>