Я хотел бы иметь несколько типов, которые имеют одну и ту же реализацию, но по-прежнему относятся к разному типу в C ++.
Чтобы проиллюстрировать свой вопрос на простом примере, я хотел бы иметь класс для яблок, апельсинов и бананов, у всех одинаковые операции и одинаковая реализация. Я бы хотел, чтобы у них были разные типы, потому что я хочу избежать ошибок благодаря безопасности типов.
class Apple {
int p;
public:
Apple (int p) : p(p) {}
int price () const {return p;}
}
class Banana {
int p;
public:
Banana (int p) : p(p) {}
int price () const {return p;}
}
class Orange ...
Чтобы не дублировать код, похоже, я мог бы использовать базовый класс Fruit и наследовать от него:
class Fruit {
int p;
public:
Fruit (int p) : p(p) {}
int price () const {return p;}
}
class Apple: public Fruit {};
class Banana: public Fruit {};
class Orange: public Fruit {};
Но тогда конструкторы не наследуются, и мне приходится их переписывать.
Есть ли какой-либо механизм (определения типов, шаблоны, наследование ...), который позволил бы мне легко иметь один и тот же класс с разными типами?
Ответы:
Распространенный метод - иметь шаблон класса, в котором аргумент шаблона просто служит уникальным токеном («тегом»), чтобы сделать его уникальным типом:
template <typename Tag> class Fruit { int p; public: Fruit(int p) : p(p) { } int price() const { return p; } }; using Apple = Fruit<struct AppleTag>; using Banana = Fruit<struct BananaTag>;
Обратите внимание, что классы тегов даже не нужно определять, достаточно объявить уникальное имя типа. Это работает, потому что тег фактически не используется где-либо в шаблоне. И вы можете объявить имя типа внутри списка аргументов шаблона (подсказка к @Xeo).
using
Синтаксис C ++ 11. Если вы застряли на C ++ 03, напишите вместо этого:typedef Fruit<struct AppleTag> Apple;
Если общие функции занимают много кода, это, к сожалению, приводит к появлению большого количества повторяющегося кода в конечном исполняемом файле. Этого можно избежать, имея общий базовый класс, реализующий эту функциональность, а затем имея производную от него специализацию (которую вы фактически создаете).
К сожалению, это требует от вас повторной реализации всех ненаследуемых членов (конструкторов, присваивания…), что само добавляет небольшие накладные расходы - так что это имеет смысл только для больших классов. Здесь это применимо к приведенному выше примеру:
// Actual `Fruit` class remains unchanged, except for template declaration template <typename Tag, typename = Tag> class Fruit { /* unchanged */ }; template <typename T> class Fruit<T, T> : public Fruit<T, void> { public: // Should work but doesn’t on my compiler: //using Fruit<T, void>::Fruit; Fruit(int p) : Fruit<T, void>(p) { } }; using Apple = Fruit<struct AppleTag>; using Banana = Fruit<struct BananaTag>;
источник
Fruit<struct SomeTag>
.Используйте шаблоны и используйте свойство для каждого фрукта, например:
struct AppleTraits { // define apple specific traits (say, static methods, types etc) static int colour = 0; }; struct OrangeTraits { // define orange specific traits (say, static methods, types etc) static int colour = 1; }; // etc
Затем создайте один
Fruit
класс, который набран по этому признаку, например.template <typename FruitTrait> struct Fruit { // All fruit methods... // Here return the colour from the traits class.. int colour() const { return FruitTrait::colour; } }; // Now use a few typedefs typedef Fruit<AppleTraits> Apple; typedef Fruit<OrangeTraits> Orange;
Может быть немного перебор! ;)
источник
template<class Derived> class Fruit;
источник
Также есть BOOST_STRONG_TYPEDEF .
источник