2017-12-08 2 views
1

클래스를 상속 할 때 컴파일러는이를 만들기 위해 기본 클래스의 정의를 알아야합니다. 그러나 자신 (상속 클래스)을 사용하여 템플릿 클래스를 상속하면 컴파일러는 어떻게 코드를 만들 수 있습니까? 그것은 아직 학급의 크기를 모른다. 상속 클래스를 사용하여 템플릿 클래스에서 상속

#include <iostream> 

template <class T> class IFoo 
{ 
public: 
    virtual T addX(T foo, double val) = 0; 
    // T memberVar; // uncomment for error 
}; 

class Foo : public IFoo<Foo> 
{ 
public: 
    Foo(double value) 
     : m_value(value) {} 

    Foo addX(Foo foo, double b) override 
    { 
     return Foo(foo.m_value + b); 
    } 

    double m_value; 
}; 

int main() 
{ 
    Foo foo1(1); 
    Foo foo2 = foo1.addX(foo1, 1); 

    std::cout << foo2.m_value; 
} 

는 먼저 나는 인터페이스이다 그러나 또한 일반 클래스와 함께 작동하기 때문에 작동 생각했다.

템플릿을 템플릿으로 저장할 때 예상대로 Foo가 정의되지 않았다는 오류가 발생합니다.

+0

'memberVar'는'Foo' (간접적으로) 자신의 다른 인스턴스를 보유하게 만듭니다. 본질적으로 그것은'sizeof (Foo) == 2 * sizeof (Foo)'를 만들 것이다. 물론 작동하지 않습니다.:-) –

+0

좋은 답변을 해주셔서 감사합니다. 이제 나에게 분명하다. – jaba

답변

2

여기에 일반적인 개념은 호기심 반복 템플릿 패턴 또는 CRTP이라고합니다. 검색하면 많은 조회가 발생합니다. 참조 : https://stackoverflow.com/questions/tagged/crtp.

그러나 CRTP에 지나치게 집중하지 않고 질문에 대한 답변을 제공하는 간단한 설명이 있습니다. 다음은 C와 C++에서 허용된다

struct foo { 
    struct foo *next; 
    ... 
}; 

또는 두 가지 유형 :

struct foo; 
struct bar; 

struct foo { 
    struct bar *first; 
    ... 
}; 

struct bar { 
    struct foo *second; 
    ... 
}; 

너무 오래 사용하는 struct 또는 class에 관해서 만 포인터 유형의 완전한 정의는 '아무튼 사용할 수 있어야합니다. 템플릿은 다양한 방법으로 템플릿 위에 레이어 할 수 있으며 템플릿을 매개 변수화하는 유형과 템플릿 내에서 사용하는 방법에 대해 별도로 판단해야합니다. SFINAE (대체 오류는 오류가 아님)에 추가하면 주어진 유형으로는 작업을 수행 할 수 없기 때문에 인스턴스화하지 않는 템플릿을 만들 수도 있습니다.

2

이 정의가 template class IFoo 인 경우 컴파일러는 을 레이아웃하기 위해 Foo의 크기를 알 필요가 없습니다.

Foo은이 컨텍스트에서 불완전한 클래스이며 ("undefined"또는 "undeclared"가 아님) 불완전한 유형을 사용할 수있는 방법으로 사용할 수 있습니다. 멤버 함수 매개 변수 목록에 나타나면 문제가 없습니다. 멤버 변수를 Foo*으로 선언하는 것이 좋습니다. 멤버 변수를 Foo으로 선언하는 것은 금지됩니다 (완전한 유형 필요).

1

어떻게 컴파일러가 코드를 만들 수 있습니까?

이 질문에 대답하는 것은이 질문에 대답 같은 것 어떻게 컴파일러는 그 를 컴파일 할 수 있습니까?

struct Type; 
Type func(Type); 

Live example

당신은 어떻게 존재하고 아직 유형을 사용하는 함수를 선언하지 않는 유형을 정의 할 수 있습니까?

대답은 간단합니다. 컴파일 할 코드가 실제로 존재하지 않는 유형을 사용합니다. 컴파일 할 코드가 없으므로 어떻게 실패 할 수 있습니까?

이제 코드와 관련하여 궁금한 점이 있습니까? 클래스가 템플릿 매개 변수로 자신을 부모에게 보낼 수있는 방법은 무엇입니까?

struct Foo : IFoo<Foo> { /* ... */ }; 

첫째, 컴파일이보고 :

컴파일러는 당신이 그 일을 할 때 무엇을 볼 수의 분석하자

struct Foo ... 

컴파일러는 이제 Foo의 존재를 알고, 아직 불완전한입니다 유형.

... : IFoo<Foo> ... 

그것은 IFoo이 무엇인지 알고, 그것은 Foo이 유형 것을 알고있다 :

지금, 그는 것으로 본다. 컴파일러는 현재 해당 유형 IFoo을 실체화 할 수 있습니다

template <class T> struct IFoo 
{ 
    virtual T addX(T foo, double val) = 0; 
}; 

그래서 정말, 그것에서 함수의 선언, 클래스를 선언합니다. 위에서 불완전한 타입의 함수를 선언하는 것이 효과가 있다는 것을 알았습니다. 여기에서도 마찬가지입니다. 이 코드는 다음과 같으므로 해당 코드는 가능합니다.

struct Foo; 
template struct IFoo<Foo>; // instanciate IFoo with Foo 

그래서 실제로는 마법이 없습니다.


이제 더 확실한 예를 들어 보겠습니다. 그게 뭐야?

template<typename T> 
struct IFoo { 
    void stuff(T f) { 
     f.something(); 
    } 
}; 

struct Foo : IFoo<Foo> { 
    void something() {} 
}; 

어떻게 할 수있는 불완전한 형태의 컴파일러 호출 something?

것은 : 그렇지 않습니다. 을 사용하면 Foo이 완료됩니다. 이것은 템플릿 함수가 사용될 때만 인스턴스화되기 때문입니다.

템플릿과 함수 정의를 구분할 수 있습니다.

template<typename T> 
struct IFoo { 
    void stuff(T f); 
}; 

template<typename T> 
void IFoo<T>::stuff(T f) { 
    f.something(); 
} 

struct Foo : IFoo<Foo> { 
    void something() {} 
}; 

위대한! 그것은 순수한 가상 함수로 당신의 예제와 똑같이 보이기 시작합니까? 다른 유효한 변환을 시도해 보겠습니다.

template<typename T> 
struct IFoo { 
    void stuff(T f); 
}; 

struct Foo : IFoo<Foo> { 
    void something() {} 
}; 

// Later... 
template<typename T> 
void IFoo<T>::stuff(T f) { 
    f.something(); 
} 

완료! Foo이 완료된 후에 함수를 나중에 정의했습니다. 그리고 이것은 무슨 일이 일어나는가입니다 : 컴파일러는 IFoo<Foo>::stuff을 사용할 때만 instanciate합니다. 그리고 사용 된 지점은 Foo입니다. 그곳에는 마법도 없습니다.


왜 다음 IFoo 내부 T 멤버 변수를 선언 할 수 없습니다?

간단하고, 같은 이유로이 코드는 컴파일되지 않습니다 이유 : 그것은 불완전한 유형의 변수를 선언 이해가되지 않습니다

struct Bar; 
Bar myBar; 

.