Доброе утро,
У меня есть шаблонизированный класс, и я хочу манипулировать объектами с помощью вектора указателей. Чтобы использовать вектор указателей на шаблонизированный класс, мне нужно, чтобы этот класс был производным от не шаблонизированного класса, и я сделал это.
Вот моя проблема: чтобы вызвать метод производного класса из указателя на базовый класс, я не могу использовать виртуальные функции, потому что функции шаблона нельзя сделать виртуальными. Мне нужно сделать явное приведение, что утомительно: после создания числового объекта с новым, на самом деле, нужно уменьшить число до номера *, хотя известно, что объект заранее является числом.
Я решил эту проблему неловко: функция myset проверяет все поддерживаемые значения typeid, чтобы получить правильное динамическое приведение. Это длинный ряд вложенных if, которые выполняют проверку typeid.
Помимо утомления, функция работает только для вызова метода set, и аналогичные функции должны быть определены для вызова других методов. Если бы я мог сделать автоматическое приведение к объекту, на который я указываю, все было бы намного проще.
Минусы подхода:
Компилятор знает, что lst [0] (в приведенном ниже коде) указывает на объект числа, хотя lst является вектором объектов-элементов, из которых число<> объекты являются производными.
Существует ли метод для автоматического уменьшения указателя, определенного как base *, на указатель на объект, на который фактически указывается?
Если бы у меня был правильный downcasting, то я мог бы определить тысячи методов в числе<> класс и вызвать их с помощью правильного вещания -> вызов функции (…)
Вот код (он работает правильно, ядро - это определение «myset»)
Заранее спасибо,
Пьетро М.
PS Меня больше всего интересует стандартный подход C ++ без использования других библиотек, кроме STL, таких как Boost.
#include <iostream>
#include <vector>
#include <typeinfo>
using namespace std;
class element
{
public:
virtual void print() = 0; // print is not templatized and works properly
//template <class T> virtual set(T v) = 0; // this would solve all my problems, if only it were legal.
};
template <class T>
class number : public element
{
T val;
public:
void print() {cout << "number is " << val << endl;}
void set(T v) {val = v;}
};
// That's the best I can do!
template <class T>
void myset(T v, element *ptr)
{
// There is a kink in the template: the compiler checks the T type by the value of v, that in this case is an integer:
// cout << "Type name for the template is: " << typeid(T).name() << endl;
// cout << "Type name for an integer is: " << typeid(int).name() << endl;
if (typeid(*ptr) == typeid(number<double>))
{
((number<double> *) ptr) -> set(7);
return;
}
else if (typeid(*ptr) == typeid(number<float>))
{
((number<float> *) ptr) -> set(7);
return;
}
// add other types... (tedious)
else
{
cout << "type not supported" << endl;
}
}
int main()
{
vector <element *> lst; // list of heterogeneous templatized objects
lst.push_back(new number<float>);
lst.push_back(new number<double>);
lst[0] -> print();
//((number<float> *) lst[0]) -> set(7); it's correct but it requires I know the type when I call it (tedious)
myset(7, lst[0]); // may be inefficient, but it works (for the types which are explicitly supported)
// cast_to_what_the_pointer_actually_points_to <lst[0]> -> set(7); // that's what I'd like to do: a downcast function which checks the object type and returns the correct pointer would be able to call any class method...
lst[0] -> print();
}
Ты почти у цели. Вам нужно иметь шаблон set
метод, который вызывает частный виртуальный метод с typeid
и void *
указатель на его аргумент, позволяющий переопределению решить, как его обработать:
class element
{
virtual void set_impl(const std::type_info &, const void *) = 0;
public:
template <class T> void set(T v) { set_impl(typeid(v), &v); }
};
И пример того, как написать set_impl
:
template <class T>
class number : public element
{
T val;
void set_impl(const std::type_info &ti, const void *pv) {
if (ti == typeid(T)) {
val = *static_cast<const T *>(pv);
} else {
throw std::invalid_argument("incorrect type to set()");
}
}
};
Это похоже на подход, принятый Boost.Any.
Других решений пока нет …