如何在C ++中为同一类定义不同的类型


84

我想有几种共享相同实现的类型,但在C ++中仍然是不同的类型。

为了用一个简单的例子说明我的问题,我想有一个针对Apple,Oranges和Bananas的类,它们都具有相同的操作和相同的实现。我希望它们具有不同的类型,因为由于类型安全,我想避免错误。

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 {};

但是,构造函数不会被继承,因此我必须重写它们。

是否有任何机制(typedef,模板,继承...)可以使我轻松地拥有具有不同类型的同一个类?


您能否更详细地说明为什么需要这样做?我不能提出任何好主意。如果这些类共享实现,这是否意味着它们也共享功能?
jnovacho

4
是的,但是由于它们的类型不同,因此在编译时可以检测到一些编程错误(例如,合并Apple和Orange)。
anumi

Answers:


119

一种常见的技术是拥有一个类模板,其中template参数仅充当唯一标记(“标记”)以使其成为唯一类型:

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>;

注意,甚至不需要定义标签类,声明一个唯一的类型名就足够了。这是因为标签ISN实际上使用的模板中的任何地方。你可以声明的类型名称里面的模板参数列表(帽尖到@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>;

+1,如果您不想为单个水果定义任何其他属性,我将继续使用……
Nim 2013年

20
实际上,您可以在模板参数列表中声明它们,我发现这很方便:Fruit<struct SomeTag>
Xeo

1
@KonradRudolph很遗憾我不能+1编辑本身.....我看到了编辑评论
eternalmatt

1
@eternalmatt大声笑–我永远都不会想到任何人都会看到这一点。但是,即使没有人看,您也必须保持幽默。;-)
Konrad Rudolph

2
不利的一面是针对不同类型的模板实例的多次发射。这些重复项是否已被广泛使用的链接器消除?
boycy

19

使用模板,并对每个水果使用一个特征,例如:

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;

可能有点过大了!;)



By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.