Автоматическое понижение указателя на производный объект

Доброе утро,

У меня есть шаблонизированный класс, и я хочу манипулировать объектами с помощью вектора указателей. Чтобы использовать вектор указателей на шаблонизированный класс, мне нужно, чтобы этот класс был производным от не шаблонизированного класса, и я сделал это.

Вот моя проблема: чтобы вызвать метод производного класса из указателя на базовый класс, я не могу использовать виртуальные функции, потому что функции шаблона нельзя сделать виртуальными. Мне нужно сделать явное приведение, что утомительно: после создания числового объекта с новым, на самом деле, нужно уменьшить число до номера *, хотя известно, что объект заранее является числом.

Я решил эту проблему неловко: функция myset проверяет все поддерживаемые значения typeid, чтобы получить правильное динамическое приведение. Это длинный ряд вложенных if, которые выполняют проверку typeid.

Помимо утомления, функция работает только для вызова метода set, и аналогичные функции должны быть определены для вызова других методов. Если бы я мог сделать автоматическое приведение к объекту, на который я указываю, все было бы намного проще.

Минусы подхода:

  1. код повторяется: если бы я определил другую функцию (например, T get () {return val;}, мне понадобилась бы другая функция myget с полным набором вложенных ifs!
  2. список поддерживаемых типов должен быть явно определен вложением, если вызовы
  3. код может быть неэффективным

Компилятор знает, что 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();
}

4

Решение

Ты почти у цели. Вам нужно иметь шаблон 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.

3

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]