C ++: Полиморфный шаблон класса - PullRequest
5 голосов
/ 11 октября 2009

Рассмотрим класс Calendar, в котором хранится группа объектов Date. Календарь предназначен для хранения коллекции объектов любого типа, которые наследуются от Date. Я думал, что лучший способ сделать это - иметь шаблон класса, такой как

template<typename D> class Calendar{ 
    ...
}

Но меня поразило, что теперь D действительно может быть любым классом. Мой вопрос сейчас, как я могу убедиться, что D является подклассом объекта даты?

Я знаю, как это сделать на Java, но я все еще незнаком с синтаксисом C ++. Проблема очень похожа на то, как некоторые коллекции могут принимать только переменные шаблона, которые реализуют Comparable. Заголовок будет выглядеть примерно так:

public class Calendar<D extends Date>{
     ...
}

-------------------- РЕДАКТИРОВАТЬ: ------------------------- -----------------

Аргумент шаблона определяет, к какому фактическому дню относится календарь. Различные типы дат относятся к одному и тому же дню в разных форматах. Например, если я сделаю Calendar<Gregorian>, он сможет брать даты в другом формате Date, скажем, в юлианском календаре или любом другом формате даты, и представлять их в григорианском формате. Это позволяет для преобразования между календарями в разных форматах даты. Итак, если у меня есть Calendar<Gregorian>, я могу легко преобразовать его в Calendar<Julian>. Тогда возможно следующее:

Calendar<Gregorian> cal;
std::cout << "These events are entered as dates in 
    the Gregorian calendar" << std::endl;
cal.add_event("Christmas", 12, 25);
cal.add_event("Gregorian new year", 1, 1);
std::cout << cal << std::endl;
std::cout << "----" << std::endl;
std::cout << "And printed out as Julian dates" << std::endl;
Calendar<Julian>(cal);
std::cout << cal<< std::endl;

и выходы:

These events are entered as dates in the Gregorian calendar
2009-12-25 Christmas
2010-01-01 Gregorian new year
----
And printed out as Julian dates
2009-12-13 Christmas
2009-12-19 Gregorian new year

------------- Новое редактирование: ----------------------

Последнее редактирование теперь имеет больше смысла. У меня было небольшое несогласие с форматированием.

Спасибо за все ответы.

Я учусь на информатике на третьем курсе, и я бы сказал, что хорошо знаком с ОО и связанными с ним понятиями, такими как полиморфизм и т. Д. Цель этого поста состояла в том, чтобы выяснить, существует ли способ в C ++, чтобы выразить условие для аргумента шаблона так же, как в Java, и решить проблему кратким, элегантным и интуитивно понятным способом.

Ответы [ 6 ]

9 голосов
/ 11 октября 2009

Я знаю, как это сделать на Java, но я все еще незнаком с синтаксисом C ++. Проблема очень похожа на то, как некоторые коллекции могут принимать только переменные шаблона, которые реализуют Comparable. Заголовок будет выглядеть примерно так:

public class Calendar<D extends Date>{
     ...
}

Правда, это та же проблема, и в C ++ ее обычно решают, игнорируя ее. Почему мы должны обеспечить, чтобы объект должен реализовать IComparable? В Java это необходимо из-за его анемичной системы типов. Без этого ограничения мы не смогли бы сравнивать объекты.

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

И вы, как правило, делаете то же самое в вашем Calendar классе. Просто не применяют форму обязательного подкласса Date ограничение.

Вместо этого укажите члены, которые тип должен предоставлять, и какую семантику от них следует ожидать.

Например, если ваш Календарь пытается выполнить следующие операции, для объектов даты d0 и d1:

d0.getDay();
d0.getTime();
Time t = d0 - d1;

Тогда эти операции должны поддерживаться. Любой класс, который поддерживает эти операции , является допустимым классом Date, даже если он не подкласс что-либо .

8 голосов
/ 11 октября 2009

Что вам нужно, так это проверки концепции аргументов шаблона. Они были частью проекта для следующего стандарта C ++, но были выброшены снова несколько недель / месяцев назад.

Без концепций в собственном языке есть несколько библиотек, которые пытаются это сделать, но причина того, что проверки концепций являются частью основного языка, заключается в том, что их более или менее невозможно реализовать без языковой поддержки.

В вашем конкретном примере это не должно быть слишком сложно. Например, вы можете поместить какой-то специальный typedef в базовый класс и проверить это:

class date {
  public:
    typedef int is_derived_from_date;
};

template<typename D> class Calendar{ 
    typedef typename D::is_derived_from_date blah;
    ...
};

Другим способом было бы выбрать любую из мета-функций is_derived<B,D>::result шаблона, плавающих в сети, и реализовать статическую проверку для этого в вашем классе Calender. Boost имеет мета-функцию is_derived и статическое утверждение.

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

3 голосов
/ 11 октября 2009

Я думаю, что ваша проблема разрешима без использования шаблонов. D всегда является производным классом Date, тогда почему бы просто не иметь коллекцию объектов Date?

2 голосов
/ 11 октября 2009

На языке C ++ это называется проверкой концепции. В C ++ общепризнанным лучшим методом является то, что наследование используется для наследования интерфейсов, а не реализации. Таким образом, вас на самом деле не очень интересует, наследует ли D от Date, но имеет ли D те элементы интерфейса Date, которые вам нужны, а именно, имеет ли он необходимые функции-члены и т. Д. это означает, что вам не нужно иметь будущие D классы, которые должны наследоваться от Date, им просто нужно реализовать определенные функции.

Проверка концепции была удалена в C ++ 0x, но вы можете найти ее в Boost.ConceptCheck (Основной сайт Boost здесь ).

Если вы действительно хотите, чтобы D наследовал от Date, вы можете использовать Boost.StaticAssert в сочетании с Boost.TypeTraits , чтобы проверить, D наследуется от Date.

1 голос
/ 11 октября 2009

Шаблоны обычно не требуют ограничений наследования / полиморфизма. Шаблон предназначен для работы с любым типом , который удовлетворяет заданным требованиям независимо от базовых типов.

template <typename T>
T clone(const T& cloneable) {
    return cloneable.create_clone();
} 

Этот код будет работать для любого типа, который поддерживает операцию create_clone(), не используется ICloneable -интерфейс!

В вашем случае этот код позволит использовать любой тип, который ведет себя как дата.

Если вы хотите полиморфизм базового класса, просто не используйте шаблоны и используйте Date*.

Обратите внимание, что если вы действительно хотите выполнить тест шаблона, вы можете попытаться привести фиктивный указатель объекта к Date*, который потерпит неудачу во время компиляции, если он не является производным от Date. Но обычно это не тот способ, которым используется код шаблона.

0 голосов
/ 11 октября 2009

Если вы хотите взаимодействовать только с объектами Date, почему бы просто не использовать простой полиморфизм и просто работать с Date * -s?

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

Что касается шаблонов, если данный тип имеет подходящий интерфейс, почему он не должен работать с Календарем? (Ну, концепции были спланированы для C ++ 0x, но были отброшены, но их основной мотивацией, казалось, было предоставление более четких сообщений об ошибках, связанных с шаблоном.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...