2015-07-28 2 views
2

У меня есть некоторые странные проблемы при реализации интрузивного контейнера на C++. Учитывая следующий упрощенный код:Ошибка использования абстрактного типа в шаблоне C++

struct IFace { 
    virtual int foo() const = 0; 
}; 

struct Impl : public IFace { 
    int foo() const { return 111; } 
}; 

template <typename T> struct IntrusiveHook { 
    T item; 
    IntrusiveHook<T>* next; 
}; 

template <typename T> struct IntrusiveList { 
    IntrusiveList() : head(0), tail(0) {} 

    template <typename U> 
    void add(IntrusiveHook<U>& addItem) { 
    if (0 == head) { 
     head = reinterpret_cast<IntrusiveHook<T>*>(&addItem); 
     tail = head; 
    } 
    else { 
     tail->next = reinterpret_cast<IntrusiveHook<T>*>(&addItem); 
     tail = tail->next; 
    } 
    } 
    IntrusiveHook<T>* head; 
    IntrusiveHook<T>* tail; 
}; 

void testList() { 
    IntrusiveHook<Impl> impl; 
    IntrusiveList<IFace> list; 

    list.add(impl); 
    // list.head->item.foo(); 
} 

Я получаю следующее сообщение об ошибке - для «добавить» -Call (если закомментировать строку list.head->item.foo(), как указано выше):

error C2259: 'IFace' : cannot instantiate abstract class 
due to following members: 
'int IFace::foo(void) const' : is abstract 
testwas.cpp(7) : see declaration of 'IFace::foo' 
testwas.cpp(25) : see reference to class template instantiation 'IntrusiveHook<T>' being compiled 
with 
[ 
    T=IFace 
] 
testwas.cpp(41) : see reference to function template instantiation 'void IntrusiveList<T>::add<Impl>(IntrusiveHook<Impl> &)' being compiled 
with 
[ 
    T=IFace 
] 

Когда комментирует в line list.head->item.foo() Я получаю другую ошибку компиляции, предыдущая была оставлена ​​(это странное поведение и происходит как на компиляторах VC++, так и на gcc)

Проблемный вызов кажется tail->next = ... или e особенно оператор -> на абстрактный тип, используемый здесь.

So: Как исправить эту проблему?

  • добавить реализацию по умолчанию Ф.О. foo в IFace - который не то, что я хочу, но это устраняет проблему
  • вид Hacky решение: переписать в tail->next в

    reinterpret_cast<IntrusiveHook<U>*>(tail)->next = &addItem;

    tail = reinterpret_cast<IntrusiveHook<T>*>(reinterpret_cast<IntrusiveHook<U>*>(tail)->next); - но он не работает с VC++ (2010), но с gcc

  • ... или выполняя другие уродливые слепки

Но ПОЧЕМУ это проблема в первую очередь? Обычно не должно быть проблем с доступом к типу указателя с оператором «->».

Любая помощь будет оценена по достоинству. Благодаря!


Edit:

Просто для удобства я хотел бы иметь IntrusiveHook уже содержат элемент в качестве экземпляра. Это также трюк с интрузивным - когда я получил это право - просто добавить функциональность указателя к элементу без изменения самого элемента. «новый» не является вариантом, так как код должен работать во встроенной среде без new.

Также, конечно, я хотел бы использовать список с абстрактными элементами, потому что класс использования не знал бы классов реализации.

+0

IntrusiveList лист; list.add (impl); –

+1

или 'IntrusiveList list; list.add (new Impl()); 'так же, как ошибка говорит: вы не можете создавать объекты абстрактного класса. Ваш IntrusiveList не может работать с 'IFace', потому что это не полный тип, но он может работать с' IFace * '(потому что для объявления указателя вам не нужно знать полный тип) – user463035818

ответ

2

Насколько я вижу, проблема заключается в отливке до IntrusiveHook<T>* в IntrusiveList::add. Вы отливаете addItem в IntrusiveHook<IFace>. IntrusiveHook<IFace> имеет член item типа IFace. Но IFace является абстрактным, поэтому вы не можете объявить переменную с этим типом - вы должны использовать указатель или ссылку.

Таким образом, вы должны

  • изменения IntrusiveHook::item от типа T к T*,
  • добавить конструктор IntrusiveHook(T* item) : item(item) {},
  • изменить объявление о impl в testList() к IntrusiveHook<Impl> impl(new Impl)
  • и, наконец, изменить list.head->item.foo() к list.head->item->foo().

(см this code on cpp.sh.)

Вы также можете использовать IntrusiveList<IFace*> или IntrusiveList<Impl> вместо IntrusiveList<IFace>, если вы не хотите, чтобы изменить IntrusiveHook.

+0

Извините, но я не могу использовать« новый «Это было бы намного легче. Также нет необходимости создавать интрузивный тип при использовании динамической памяти. –

+0

@michael_s Я не знаком с концепцией интрузивных типов. Однако просто невозможно объявить переменную с абстрактным типом в C++. Вы должны использовать либо указатели, либо ссылки, либо не абстрактный класс. –

Смежные вопросы