2011-11-17 3 views
0

제목을 여기에 사용하는 방법을 잘 모릅니다.전달 선언성 및 결과 콜백에 대한 typedef 대신 상속 사용

template<various parameters> struct Base { ... }; 

그리고 나는 그것에서 파생 클래스의 개방형 설정 :

struct A: public Base<some arguments> { ... }; 
struct B: public Base<other arguments> { ... }; 
struct C: public Base<different arguments> { ... }; 
... 

가 중요한 것은, 파생 클래스가 아무것도 추가하지를

문제는 내가 클래스 템플릿을 가지고있다 멤버 함수가 아닌 템플릿 인자를 고쳐 쓰는 것 이상으로, 특히 멤버 변수가 아닌베이스로 전달됩니다. 따라서 이진 표현은 모두 동일해야합니다. typedef 대신 상속을 사용하는 유일한 이유는 A, B, C 등을 전방 선언 할 수 있기를 원한다는 것입니다 (또는보다 정확하게 A, B, C 등은 제공된 매크로, 그리고 그들의 편의를 위해 에 대해 forward-declare가 가능하도록하고 싶습니다. A, B, C 등을 사용하는 이유는 템플릿 인수를 모두 손으로 써야하는 것을 피하기 위해서입니다. 시간.

문제는 Base가 콜백을 매개 변수로 가지며 콜백이 매개 변수로 클래스 자체에 대한 참조를 가져야한다는 것입니다. 등

void foo (A&, ...); 
void bar (B&, ...); 

그리고 :

그래서 내가 좋아하는 콜백 함수를 기대합니다. 그리고 Base에서 콜백 함수를 구현할 때 콜백에 대한 인수로 이것을 전달하고 싶습니다. 꽤 현명하게도, C++은 파생 된 클래스가 기대되는 곳에서 기본 클래스를 전달하는 것이 일반적으로 안전하지 않기 때문에 이것을 수행하지 않습니다. 그러나이 특별한 경우에는 파생 클래스가 기본에 아무 것도 추가하지 않고 typedef 대신에 사용되기 때문에 문제가되지 않습니다.

  • 내가 '형식 정의'앞으로-으로 신고 할 것을 권장합니다

    그래서 내가이 세 가지 기준이 보인다.

  • 콜백에 기본 클래스를 전달할 수 있어야합니다.
  • 기본 클래스 대신 'typedefs'를 사용하여 콜백을 선언 할 수 있기를 원합니다. 서로 호환되지 않는

. (세 번째 기준은 클라이언트의 편의를 위해 기본 클래스의 템플릿 인수를 매번 성가 시게 길게 작성해야하므로 그렇게하지 않으려는 이유는 'typedef ') 나는 세 가지 솔루션을 참조

:.

  • 드롭 첫 번째 또는 세 번째 선정시를.

  • 콜백 함수가 기대하는 어떤 종류의 인수를 확인하고 어떤 형식의 기본 형식인지 확인합니다 (sizeof가 동일하고 SFINAE가 파생됨을 확인하여). 또는 pseudo-typedef를 생성하는 매크로에 의해 삽입 된 어떤 종류의 토큰을 검사하거나 그런 종류의 것을 체크하고 캐스트를 수행한다.

더 좋은 것들이 있습니까?

+2

Nitpick,하지만 중요한 :

그런 다음 수업이 기지에서 템플릿을 유도 당신은 템플릿에서 "을", 즉에서 파생되지 않습니다. 오히려 다양한 * 관련이없는 여러 클래스에서 파생됩니다. Base , Base 등 –

+0

글쎄요, 저는 여러 가지 인자에 적용된 템플릿의 다양한 인스턴스화에서 파생됩니다. 그것은 내가 의미했던 것입니다. – glaebhoerl

답변

4

이용하여야 CRTP idiom을 (C++ 11. 허용)와 템플릿 파라미터에 파생 된 클래스 aswell 통과. 콜백의 경우 파생 형식에 *this을 단순히 입력하면됩니다.

template<class Derived, other params...> 
struct Base{ 
    template<class F> 
    void do_callback_stuff(F func){ 
     func(static_cast<Derived&>(*this), ....); 
    } 
}; 

struct A : public Base<A, other args...>{}; 
+0

CRTP! 당연하지. 명백한 대답으로 한 번 질문하는 것이 상쾌합니다. (이것이 기본적으로 더 깨끗하고 정교한 제 3의 구체화라고 언급 할 가치가 있지만, 위에서 나온 것들을 캐스팅 해보십시오.) – glaebhoerl

+0

Derived에서 정적 검사를하고 싶기 때문에 질문을 따르십시오. (고정 된) 파생물이 파생되었는지 여부는 static_cast (Base &)가 안전한지에 대한 충분한 조건을 파생합니까? – glaebhoerl

+0

@illissius :'std :: is_base_of :: value'을 시도하십시오. ''에 있습니다. 그것만으로도 충분합니다. 'Derived '가 실제로'Base' (실제로있을 것입니다.)에서 파생되었는지 확실히하고 싶다면 간단한 static_assert를 클래스 본문에 넣으십시오. '템플릿 <...> 클래스베이스 {typedef 자료 <...> this_type; static_assert (std :: is_base_of :: 값, "잘못된 파생 된 템플릿 매개 변수!"); }; ' – Xeo

1

아마 호기심 반복 템플릿 패턴과 암시 적 변환의 조합이 트릭을 할 것입니다 : 다른 제안으로

template<typename Derived, various parameters> struct Base 
{ 
    operator Derived&() { return static_cast<Derived&>(*this); } 
    operator Derived const&() const { return static_cast<Derived const&>(*this); } 
    other stuff; 
}; 

struct A: Base<A, some arguments> {}; 
2

을 CRTP는 해결책이 될 수 있습니다.

그러나 콜백이 언제든지 왜 개체의 전용 호출자가 스스로를 자세히 알지 못하는 개체에 대한 참조를 필요로하는지 다시 생각해 봐야합니다. 나는 "클래스 템플릿이 인스턴스화되기 전에 자신의 세부 사항에 대해 많이 알지 못한다"고 말하고 싶다.

따라서 호출자 개체 참조 (인스턴스화 된 클래스 템플릿 개체)와 관련된 콜백의 사용 패턴을 확인하십시오. 공통 기본 클래스 또는 기본 인터페이스를 추출합니다.

struct BaseInterface { 

    // HERE, declare anything, that callbacks may use 

    virtual void foo() = 0; 
}; 

template<various parameters> 
struct Base : public BaseInterface { ... }; 


void some_callback(BaseInterface& caller, ...); 
+0

영감을 얻은 생각. 그러나 인터페이스를이 방법으로 제한하는 것이 합리적이라 할지라도 (하지만 그렇게 할 수는 없지만 알 수는 없습니다.) BaseInterface는 여전히 같은 방식으로 템플릿 화되어야하며 우리는 사각형으로 돌아갑니다. – glaebhoerl

관련 문제