#c #templates #virtual #abstract-class
#c #шаблоны #виртуальный #абстрактный класс
Вопрос:
У меня есть абстрактный класс Handle<T>, который содержит ссылки на объекты типа T. Я хочу, чтобы этот класс мог быть преобразован в Handle<U> , где U — суперкласс T. Я бы использовал наследование, но здесь это не работает. Как бы я это сделал? Какие есть хорошие альтернативы?
Пример псевдокода:
template<class T>
class Handle {
public:
virtual ~Handle () {}
virtual T amp; operator* () const = 0;
virtual T * operator-> () const = 0;
virtual template<class U> operator Handle<U>* () const = 0; // being lazy with dumb pointer
};
template<class T>
class ConcreteHandle : public Handle<T> {
public:
explicit template<class U> ConcreteHandle (U * obj) : obj(obj) {}
virtual ~ConcreteHandle () {}
virtual T amp; operator* () const {
return *obj;
}
virtual T * operator-> () const {
return obj;
}
virtual template<class U> operator Handle<U>* () {
return new ConcreteHandle<U>(obj);
}
private:
T * obj;
};
Как и было запрошено, это то, что я делаю
class GcPool {
public:
virtual void gc () = 0;
virtual Handle<GcObject> * construct (GcClass clazz) = 0;
};
class CompactingPool : public GcPool {
public:
virtual void gc () { ... }
virtual Handle<GcObject> * construct (GcClass clazz) { ... }
private:
Handle<GcList<Handle<GcObject> > > rootSet; // this will grow in the CompactingPool's own pool
Handle<GcList<Handle<GcObject> > > knownHandles; // this will grow in the CompactingPool's own pool.
};
knownHandles должен быть совместим с Handle, чтобы он мог находиться в корневом наборе CompatingPool. То же самое касается rootSet. Я загружу эти специальные дескрипторы, чтобы не возникло проблемы с курицей и яйцом.
Комментарии:
1. Почему бы просто не создать
operator Handle<U>()
concrete в базовом классе? Действительно ли когда-либо имело смысл предоставлять другую реализацию?2. Моему приложению нужны (читай: хочет) различные типы дескрипторов. Разные типы пулов памяти (gc) выдают разные дескрипторы. Опять же, я мог бы переусердствовать. Тем не менее, это все еще интересный вопрос для меня.
3. @Tony: Это заставило бы
class Handle
использовать его производный класс в своей реализации. Вы могли бы это сделать, но предполагая, что на самом деле существует несколько возможных классов, производных отclass Handle
, тогда это не имеет смысла. Если их нет, то для начала не должно быть полиморфизма.4. @trinithis: Рассматривали ли вы возможность использования аргумента шаблона вместо полиморфизма для управления распределителем? Обратите внимание, что это не обязательно должно быть таким сложным, как распределители STL.
5. Я пишу компилятор и хочу выполнить сборку мусора на этом языке. У меня есть GcPool как абстрактный класс. GcPool имеет чисто виртуальную функцию построения (класса), которая возвращает дескриптор<GcObject> . Я обновляю свою первую реализацию GcPool, CompactingPool, для внутреннего использования дескрипторов после небольшой начальной загрузки, чтобы он мог выделять больше дескрипторов в своем собственном пуле. Но я хочу, чтобы эти дескрипторы имели лучшие типы, чем GcObject, такие как GcStack. Возможно, это относится к явным приведениям….
Ответ №1:
virtual template<class U> operator Handle<U>* () const =0;
Виртуальная функция шаблона не разрешена спецификацией языка.
Рассмотрите этот код в ideone, а затем увидите ошибку компиляции:
ошибка: шаблоны могут быть не ‘виртуальными’
Что теперь вы можете сделать? Одно из решений заключается в следующем:
template<class T>
class Handle {
public:
typedef typename T::super super; //U = super, which is a superclass of T.
virtual ~Handle () {}
virtual T amp; operator* () const = 0;
virtual T * operator-> () const = 0;
//not a template now, but still virtual
virtual super operator Handle<super> () const = 0;
};
То есть определите typedef
базовый класс в производном классе и используйте его в Handle
. Что-то вроде этого:
struct Base {//...};
struct Derived : Base { typedef Base super; //...};
Handle<Derived> handle;
Или вы можете определить признаки, как:
struct Base {//... };
struct Derived : Base { //... };
template<typename T> struct super_traits;
struct super_traits<Derived>
{
typedef Base super;
};
template<class T>
class Handle {
public:
typedef typename super_traits<T>::super super; //note this now!
virtual ~Handle () {}
virtual T amp; operator* () const = 0;
virtual T * operator-> () const = 0;
//not a template now, but still virtual
virtual super operator Handle<super> () const = 0;
};
На мой взгляд, super_traits
это превосходное решение, поскольку вы определяете свойства производных классов без их редактирования. Кроме того, вы можете определить столько определений типов, сколько захотите; допустим, ваш производный класс имеет более одной базы, вы можете захотеть определить много определений типов или, предпочтительно, список типов.
Комментарии:
1. @Nawaz, OP хочет получить эффект от
virtual template
метода. Вот почему он упомянул как псевдокод . Можно предположить, что он должен знать об ошибке.2. @iammilind: Поскольку виртуальный шаблон невозможен, то о нем сейчас не может быть и речи. Вы не можете определить виртуальный шаблон.
3. Это повторяет известную проблему … код в вопросе указывает на желаемую функциональность — Кит знает, что это не работает. То, что он хочет, это что-то функционально эквивалентное.
4.@Nawaz, Тони имеет в виду, что спрашивающий хочет некоторую функциональность, которая имитирует
virtual template
,5. @iammilind: Все зависит от определения «некоторой функциональности». Что вы под этим подразумеваете? Если вы говорите «вся функциональность» виртуального шаблона, это не является расплывчатым, но «некоторая функциональность» является расплывчатой. Кстати, я просто пытаюсь решить эту проблему. Я не понимаю, чего это не достигает.