2009-10-11 2 views
5

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

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

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

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

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

-------------------- EDIT: -------------- ----------------------------

Аргумент шаблона определяет, к какому действительному дню относится календарь. Различные типы дат относятся к одному и тому же дню в разных форматах. Например, если я сделаю 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, и решить проблему кратким, изящным и интуитивно понятным способом.

+2

Почему это необходимо * для подкласса даты? До тех пор, пока он ведет себя как дата (раскрывает правильные члены), что не так, если рассматривать его как дату? – jalf

ответ

9

Я знаю, как это сделать, это 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, даже если он не является подклассом ничего.

+0

IMO это лучший ответ. Я бы хотел, чтобы мой ответ просто сказал, что утиная набивка в порядке. +1 – sbi

+1

+1 для использования статического полиморфизма –

3

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

+0

На самом деле, я в основном интересуюсь синтаксисом, но некоторые функции класса (нерелевантные для вопроса) также требуют, чтобы у меня был точный тип. – Nubsis

0

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

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

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

8

Что вы ищете - это проверки концепции для аргументов шаблона. Они были частью проекта для следующего стандарта 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, так и статическое утверждение.

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

+1

Re. в последнем абзаце, что не так с полиморфизмом времени компиляции шаблонов, что вы хотите использовать обычный полиморфизм OO? Я согласен, смесь неловкая. Из какой информации у нас есть, похоже, что и динамический, и статический полиморфизм могут работать. И тогда я предпочел бы статичность. – jalf

+0

@jalf: Я тоже могу. Но из вопроса, похоже, Марко уже хорошо знает полиморфизм ОО, в то время как IME многие занимают довольно много времени, чтобы понять, что такое расширенный материал шаблона и статический полиморфизм. Поэтому для начинающих C++, знающих OO, я предлагаю придерживаться этого, пока они не станут более знакомы с C++-способом его выполнения. – sbi

+0

true. Любой подход действителен. Я думаю, что маршрут шаблона был бы более идиоматическим C++, но OO один больше знаком начинающим и не-C++-программистам. Это просто сочетание двух проблем. – jalf

1

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

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

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

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

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

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

2

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

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

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

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