Обходные пути виртуальных шаблонов

#c #oop #templates #inheritance #virtual

#c #ооп #шаблоны #наследование #виртуальный

Вопрос:

У меня есть класс контейнера шаблонов, который я производный от вызываемого MyContainer . MyContainer определяет такие методы, как Get() , Set() и т.д. для доступа к отдельным элементам. Я хотел бы создать класс bitfield, реализованный как MyContainer<char> , где каждый char элемент содержит CHAR_BIT количество битов. Однако, чтобы позволить пользователю оперировать отдельными битами, а не целыми байтами, мне пришлось бы сделать Get() и Set() виртуальными, что незаконно. Каковы некоторые альтернативы?

Я думал определить GetBit() и SetBit() в производном классе, но это нарушило бы принцип подстановки Лискова. (Подумайте о SortMyContainer() функции.)

РЕДАКТИРОВАТЬ: Вот упрощенный пример:

 template <typename Datatype>
struct MyContainer
{
    virtual Datatype Get();
};

template <typename Datatype> // Error: Templates may not be virtual.
virtual Datatype MyContainer<Datatype>::Get() // EDIT: The problem was on this line.  The "virtual" keyword should only appear with the function declaration.
{
    // ...
}
  

Комментарии:

1. Могу ли я рассказать вам о шаблоне NVI или методе шаблона? Смотрите gotw.ca/publications/mill18.htm . STL часто реализуется по этому принципу.

Ответ №1:

Это не является незаконным, только виртуальные функции-члены шаблона являются.

 // valid
template<typename T> class MyContainer {
    virtual void set(const T amp;) = 0;
}

// not valid
class MyContainer {
    template <typename T> virtual void set (const T amp;) = 0;
}
  

Если я вас неправильно понял, пожалуйста, рассмотрите возможность размещения примера кода.

редактируйте после добавления вами примера кода:

 template <typename Datatype>
virtual // <-- nope, not here
Datatype MyContainer<Datatype>::Get()
{
    // ...
}
  

virtual это только часть объявления внутри тела класса. Это должно быть допустимо:

 template <typename Datatype>
Datatype MyContainer<Datatype>::Get()
{
    // ...
}
  

Однако обратите внимание, что определение должно быть видимым в момент создания экземпляра шаблона. Так что либо поместите это также в заголовочный файл (или в дополнительный заголовок, который вы затем включаете в свой реальный заголовок), либо оставьте это в теле класса.

(пожалуйста, сейчас никто не упоминает export шаблоны ed, мы с вами их хорошо знаем, но они не совсем для начинающих и устарели со следующим стандартом)

Комментарии:

1. Однако функции-члены являются шаблонами; они используют параметр шаблона класса в возвращаемых значениях и параметрах.

2. @Maxpm: Это не имеет значения. Если они сами по себе не имеют параметров шаблона, то они не являются шаблонами.

3. @phresnel @DeadMG Я добавил упрощенный пример в OP.

4. @Maxpm: Если ваш компилятор отклоняет этот код, значит, это неправильно . О, помимо того факта, что вы не должны помещать virtual туда.

5. @DeadMG: Иногда компиляторы ошибаются, особенно очень устаревшие. @Maxpm: Как указывает DeadMG, при переопределении функций-членов virtual должно быть опущено. Это только часть объявления внутри тела класса.

Ответ №2:

Похоже, вы не понимаете, что представляет собой шаблон. Шаблоны классов могут иметь виртуальные функции, и действительно, эти параметры шаблона могут отображаться в сигнатурах этих функций.

 template<typename T> class an_interface {
    virtual T Get() = 0;
};
class a_class : public an_interface<int> {
};
  

Это совершенно справедливо. Что не совсем корректно, так это

 class an_interface {
    template<typename T> virtual T Get() = 0;
}
  

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