1. ホーム
  2. c++

[解決済み] C++で同じクラスに対して異なる型を定義する方法

2023-05-09 13:17:53

質問

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

でもそうすると、コンストラクタが継承されないので、書き直さなければなりません。

同じクラスで異なる型を簡単に持てるような仕組み(typedef、テンプレート、継承...)はないのでしょうか?

どのように解決するのですか?

一般的なテクニックは、クラステンプレートを持つことで、テンプレートの引数が単にユニークなトークン(「タグ」)として機能し、ユニークな型となることです。

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