2011-02-05 4 views
2

템플릿 매개 변수가 관련된 경우 한 개체를 다른 개체로 캐스팅 할 수있는 방식으로 클래스 템플릿을 구현할 수 있습니까?C++에서 템플릿 클래스 공분산을 구현하는 방법은 무엇입니까?

struct Base {}; 
struct Derived : Base {}; 

template <typename T> class Foo { 
    virtual ~Foo() {} 
    virtual T* some_function() = 0; 
}; 

Foo<Derived>* derived = ...; 
Foo<Base>* base = derived; 

여기에 또 다른 문제는 푸 T & 및 T *를 리턴하는 함수를 포함하는 인터페이스로 사용되는 추상 클래스는 것을, 그래서 I : 여기에 아이디어를 보여줄 수있는 exaple이다 (물론 그것은 컴파일되지 않습니다) 템플릿 복사 생성자를 구현할 수 없습니다.

std::list<Derived> l; 
MyIterator<Base> it(l.begin()); 

UPD :

나는 그것이 다형성 싶습니다 삭제를 입력 할 수있는 STL 반복자를 개최하고, 추가 할 수있는 보편적 인 반복자 클래스를 쓰고 있어요, 즉 I는 다음과 같이 쓸 수있다 그건 내 실수 였고, MyIterator를 구현하기 위해 Foo *에 Foo *를 캐스팅 할 필요가 없었기 때문에 더 이상 문제가 아닌 것으로 생각합니다.

+1

나는 'dynamic_cast'가 당신이 원하는 것을 할 것이라고 대단히 의심합니다. – aschepler

+0

죄송합니다. 당신 말이 맞아요, 그것은 NULL을 돌려주고 있어요 :) – lizarisk

답변

4

템플릿 인수는 사용자가 가리키는 객체의 내용과 아무 관련이 없습니다. 이것이 작동 할 이유가 없습니다. 당신은 결국 정말 a에 존재하지 않는 정수 bar에 액세스 할

struct Base { }; 
struct Derived : Base {}; 

template<typename T> struct A { int foo; }; 
template<> struct A<Base> { int foo; int bar; }; 

A<Derived> a; 
A<Base> *b = &a; // assume this would work 
b->bar = 0; // oops! 

설명하기 위해!


이제 더 많은 정보를 제공 했으므로 이제 완전히 다른 것을하고 싶습니다. 여기에 몇 가지 스타터는 다음과 같습니다

template<typename T> 
struct MyIterator : std::iterator<...> { 
    MyIterator():ibase() { } 
    template<typename U> 
    MyIterator(U u):ibase(new Impl<U>(u)) { } 
    MyIterator(MyIterator const& a):ibase(a.ibase->clone()) 

    MyIterator &operator=(MyIterator m) { 
    m.ibase.swap(ibase); 
    return *this; 
    } 

    MyIterator &operator++() { ibase->inc(); return *this; } 
    MyIterator &operator--() { ibase->dec(); return *this; } 
    T &operator*() { return ibase->deref(); } 
    // ... 

private: 
    struct IBase { 
    virtual ~IBase() { } 
    virtual T &deref() = 0; 
    virtual void inc() = 0; 
    virtual void dec() = 0; 
    // ... 

    virtual IBase *clone() = 0; 
    }; 
    template<typename U> 
    struct Impl : IBase { 
    Impl(U u):u(u) { } 
    virtual T &deref() { return *u; } 
    virtual void inc() { ++u; } 
    virtual void dec() { --u; } 
    virtual IBase *clone() { return new Impl(*this); } 
    U u; 
    }; 

    boost::scoped_ptr<IBase> ibase; 
}; 

이 그런 다음 당신은 any_iterator을 조사 할 수 있습니다

MyIterator<Base> it(l.begin()); 
++it; 
Base &b = *it; 

로 사용할 수 있습니다. 약간의 행운을 가지고, 목적에 맞게 템플릿을 사용할 수 있습니다 (테스트하지는 않았습니다).

+0

오, 너무 간단! 거의 똑같은 코드를 작성했지만 IBase와 Impl을 전역 클래스 (중첩되지 않음)로 작성했습니다. Impl이 Ibase :: value_type>을 상속했기 때문에 실수를 저질렀습니다. 그것은 T와는 다르므로 포인터는 공변 적이 지 않습니다. 이제 MyIterator 안에 넣어 놨습니다. :) – lizarisk

+0

MyIterator가'operator =='를 구현하는 방법에 대한 아이디어는'MyIterator :: Impl :: const_iterator>'이고 다른 하나는'MyIterator :: Impl :: iterator>'입니까? – aschepler

+0

@aschepler 필자는 any_iterator 문서가 정확하다고 생각합니다. (동일한 impls를 비교할 수 있도록 허용하고 typeid를 사용하여 검사 한 경우에만) 안전성을 구현합니다.이 경우 구현이 불가능합니다. 또는 한쪽을 다른쪽에 static_cast로 지정하면 두 반복자 모두 기본 레이아웃이 같고 다른 유형으로 해석 될 때 "작동"한다고 가정해야합니다. –

1

Foo<Derived>Foo<Base>을 상속하지 않으므로 후자로 변환 할 수 없습니다. 또한 가정이 잘못되었습니다. dynamic_cast이 실패합니다.

Foo<Derived> 인스턴스를 복사하는 Foo<Base> 인스턴스의 새 개체를 만들 수 있지만 지금은 사용자가 찾고있는 것 같습니다.

4

다른 종류의 관계가 이러한 종류의 관계가 템플릿과 함께 "내장"되어 있지 않다고 지적한 반면, 이러한 종류의 관계로 작동하는 기능을 빌드하는 것이 가능하다는 점에 유의해야합니다. 예를 들어, boost::shared_dynamic_cast and friends

class A { ... }; 
class B : public A { ... }; 

당신이 boost::shared_ptr<A>boost::shared_ptr<B> 사이 치라 주어진.

이와 비슷한 작업을 수행하는 경우 MyIterator가 지원하는 작업에주의해야합니다. 예를 들어, MyIterator<Base>(std::list<Derived>::iterator)의 귀하의 예제를 사용하여, 당신은

*myIter = someBaseValue; 

컴파일되지해야한다, 예를 들어, operator*()의 좌변 버전이 안된다.

+0

iterator 예제의 starter-implementation은 역 참조 연산자에서 lvalues를 반환합니다. 그게 합리적인 일이라고 생각합니다. –

+0

프로토 타입 반복자 (포인터)는 분할 된 할당 (기본 클래스에 공용 할당이있는 경우)을 허용합니다. 클래스 템플릿이 그것을 지원하고 프로그래머에게주의를 기울여야하는 것은 합리적이라고 생각합니다. – aschepler

+0

어깨를 으.. 어쩌면 더 나은 인수는 "상속 및 할당 믹싱을 피하는 것"입니다. 이 예제는 실제로 슬라이스되지 않지만, 파생 된 부분에베이스를 지정하고, 파생 된 부분은베이스로 지정하지 않습니다. –

관련 문제