C ++에서 동일한 클래스에 대해 서로 다른 유형을 정의하는 방법


84

동일한 구현을 공유하지만 C ++에서 여전히 다른 유형 인 여러 유형을 갖고 싶습니다.

간단한 예제로 내 질문을 설명하기 위해 Apples, 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
예,하지만 유형이 다르기 때문에 컴파일 시간에 일부 프로그래밍 오류가 감지 될 수 있습니다 (예 : Apples 및 Oranges 병합).
anumi

답변:


119

일반적인 기술은 템플릿 인수가 고유 한 유형을 만들기 위해 단순히 고유 한 토큰 ( "태그") 역할을하는 클래스 템플릿을 갖는 것입니다.

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

+1, 개별 과일에 대해 추가 속성을 정의하지 않으려면이 옵션을 사용하겠습니다.
Nim

20
실제로 템플릿 인수 목록 내에서 선언 할 수 있습니다 Fruit<struct SomeTag>..
Xeo

1
@KonradRudolph 편집 자체를 +1 할 수 없다는 것이 아쉽습니다 ..... 편집 코멘트를 봤습니다 .
eternalmatt

1
@eternalmatt LOL – 누구도 그것을 볼 것이라고는 생각하지 못했을 것입니다. 하지만 아무도 보지 않을 때도 재미 있어야합니다. ;-)
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;

약간 과잉 일 수 있습니다! ;)



당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.