Подтвердить что ты не робот

С++ Один std::vector содержащий класс шаблонов нескольких типов

Мне нужно хранить несколько типов класса шаблона в одном векторе.

Например, для:

template <typename T>
class templateClass{
     bool someFunction();
};

Мне нужен один вектор, который будет хранить все:

templateClass<int> t1;
templateClass<char> t2;
templateClass<std::string> t3;
etc

Насколько я знаю, это невозможно, если это может кто-то сказать, как?

Если это невозможно, кто-нибудь может объяснить, как сделать следующую работу?

В качестве работы я попытался использовать базовый класс non template и наследовать класс шаблона из него.

 class templateInterface{
     virtual bool someFunction() = 0;
 };

 template <typename T>
 class templateClass : public templateInterface{
     bool someFunction();
 };

Затем я создал вектор для хранения базового класса "templateInterface":

std::vector<templateInterface> v;
templateClass<int> t;
v.push_back(t);

Это вызвало следующую ошибку:

error: cannot allocate an object of abstract type 'templateInterface'
note: because the following virtual functions are pure within 'templateInterface'
note: virtual bool templateInterface::someFunction()

Чтобы исправить эту ошибку, я сделал функцию в templateInterface не чистой виртуальной, предоставив тело функции, которое скомпилировано, но при вызове функции overide не используется, а вместо тела в виртуальной функции.

Например:

 class templateInterface{
     virtual bool someFunction() {return true;}
 };

 template <typename T>
 class templateClass : public templateInterface{
     bool someFunction() {return false;}
 };

 std::vector<templateInterface> v;
 templateClass<int> i;
 v.push_back(i);
 v[0].someFunction(); //This returns true, and does not use the code in the 'templateClass' function body

Есть ли способ исправить это, чтобы использовать переопределенную функцию или есть ли другой способ обхода нескольких типов шаблонов в одном векторе?

4b9b3361

Ответ 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>>

Ответ 2

Полиморфизм работает только через указатели или ссылки. Вы будете нужна база без шаблонов. Кроме того, вам нужно будет решить где будут жить реальные объекты в контейнере. Если они все статические объекты (с достаточным временем жизни), просто используя a std::vector<TemplateInterface*> и вставка с v.push_back(&t1); и т.д., должны сделать трюк. В противном случае, вы, вероятно, захотите поддержать клонирование и сохранить клоны в вектор: желательно с контейнерами указателя Boost, но std::shared_ptr.

Ответ 3

Решения, данные до сих пор, хороши, хотя имейте в виду, что в случае, если вы возвращаете тип шаблона, отличный от bool в вашем примере, ни один из них не помог бы, так как слоты vtable не могли бы быть измерены перед началом работы. Существуют фактические ограничения с точки зрения дизайна для использования полиграфического решения, ориентированного на шаблон.

Ответ 4

Если вы ищете контейнер для хранения нескольких типов, вам следует изучить вариант boost из популярной библиотеки boost.