Специализация шаблона - лучший ответ, и он будет работать хорошо, если вы не смешиваете типы узлов. Однако, если вы хотите смешать типы связанных узлов, позвольте мне показать вам, как это сделать. Во-первых, не существует простого шаблонного решения. Из-за строгих ограничений на типы вам придется печатать ваши связанные узлы вместе.
Довольно распространенным решением является создание класса вариантов (который может содержать одно значение с типами вариантов и всегда знает, какой из них). Например, у Qt есть класс QVariant . Boost имеет boost :: any .
Вот полный пример реализации с использованием пользовательского класса вариантов, который может содержать любой из ваших типов. Я могу обработать предложенный вами указатель объекта и перечисление, но может быть расширен для большего хранения
Пример, который напечатает «delete obj» один раз:
#include <iostream>
int
main( int argc, char **argv )
{
LinkedList<VariantExample> elementObj( new ExampleObj );
LinkedList<VariantExample> elementEnum( enumOne );
elementEnum.setNext( elementObj );
}
// VariantExample class. Have a look at [QVariant][4] to see how a fairly
// complete interface could look like.
struct ExampleObj
{
};
enum ExampleEnum
{
enumOne,
enumTwo
};
struct VariantExample
{
ExampleObj* obj; // or better boost::shared_ptr<ExampleObj> obj
ExampleEnum en;
bool is_obj;
bool is_enum;
VariantExample() : obj(0), is_obj(false), is_enum(false) {}
// implicit conversion constructors
VariantExample( ExampleObj* obj_ ) : is_obj(true), is_enum(false)
{ obj = obj_;
}
VariantExample( ExampleEnum en_ ) : obj(0), is_obj(false), is_enum(true)
{ en = en_;
}
// Not needed when using boost::shared_ptr above
void
destroy()
{
if( is_obj && obj )
{
std::cout << "delete obj" << std::endl;
delete obj;
}
}
};
// The linked list template class which handles variant classes with a destroy()
// method (see VariantExample).
template
<
typename _type_ = VariantExample
>
struct LinkedList
{
LinkedList* m_next;
_type_ m_variant;
explicit
LinkedList( _type_ variant_ ) : m_next(0), m_variant( variant_ ){ }
void
setNext( LinkedList& next_ ){ m_next = &next_; }
// Not needed when using boost::shared_ptr above
~LinkedList()
{
m_variant.destroy();
}
};
Поскольку метод destroy для elementObj вызывается один раз, когда вызывается деструктор LinkedList, вывод «delete obj» появляется только один раз. Опять же, поскольку вы были достаточно конкретны в отношении удаления / владения, в этом примере есть метод / интерфейс уничтожения. Он будет явно вызван в деструкторе класса LinkedList. Лучшая модель собственности может быть реализована с помощью т.е. повышение :: shared_ptr . Тогда вам не нужно уничтожать его вручную. Кстати, помогает прочитать о конструкторах преобразования .
// the first parameter becomes boost::shared_ptr<ExampleObj>( new ExampleObj ) )
// and is deleted when LinkedList is destroyed. See code comments above.
LinkedList<> elementObj( new ExampleObj );
Наконец, обратите внимание, что у вас должен быть один вариантный класс для хранения всех ваших типов, которые могут появиться в вашей цепочке LinkedList. Два разных типа LinkedList Variant не будут работать, опять же, наконец, из-за «следующего» типа указателя; который был бы не совместим.
Сноска:
Как ограничения типа мешают простому решению? Представьте, что указатель типа «следующий» для связанного узла - это не просто пустое имя шаблона, это ярлык, но на самом деле он квалифицирован, включая аргументы шаблона - что в итоге становится символом типа, который компилятор использует для оценки совместимости типов.