7

C++에서 다중 가상 상속을 가진 가변 템플릿을 사용하여 유형을 단일 구조 정의로 집계합니다. 여기 C++ 11 가변 인자 템플릿 인자에서 중복 엔트리 제거

는 구조의 샘플 집합은 다음과 같습니다

struct meas { int i; }; 
struct meas2 : public virtual meas { int j; }; 
struct meas3 : public virtual meas { int k; }; 
그때이 사용하여 여러 가상 상속 집계

:

template <typename... Args> 
struct zipper : public virtual Args... {}; 

을 나는 다음 수행 할 수 있습니다

typedef zipper<meas, meas2> meas_type; 
meas* m = new meas_type; 

이 다음 수 캐스케이드 :

GDB에 의한

$46 = (zipper<meas3, zipper<meas, meas2> >) { 
    <meas3> = { 
    <meas> = { 
     i = 0 
    }, 
    members of meas3: 
    _vptr.meas3 = 0x400ec8, 
    k = 0 
    }, 
    <zipper<meas, meas2>> = { 
    <meas2> = { 
     members of meas2: 
     _vptr.meas2 = 0x400ee0, 
     j = 6299120 
    }, 
    members of zipper<meas, meas2>: 
    _vptr.zipper = 0x400eb0 
    }, <No data fields>} 

: 328,461,849,948,

결과 객체 그러나 오히려 다루기이다.

같은 기본 유형을 압축하려고 보조 문제도있다 :

typedef zipper<meas, meas> meas_type2; 

(가) 위의 컴파일러 오류가 발생 4.6.3 ++ G에서 "중복 기본 클래스 '신체의 체중 측정은'유효하지 않습니다."

문제는 이렇게 두 가지이다 : zipper<meas3, meas2>zipper<meas3, zipper<meas, meas2>> 변환하는 방법은

  1. 있습니까?
  2. 형식 목록에서 중복 항목을 제거하는 방법이 있습니까?

고마워요!

+1

당신은 평평뿐만 아니라 중복을 제거 할. – Nawaz

+0

정확히 @Nawaz! –

+0

질문 1에 대해, 'zipper '로 변환하겠습니까? 당신은'meas' 부분을 떨어 뜨린 것 같습니다. –

답변

7

이 문제를 해결하기위한 내 전략은 몇 가지 수준의 간접 지정을 사용하는 것입니다.

  • 지퍼 < 인수 ...> 상속에 의해 기능 process_zipper_arguments에 인수의 치료를 전달합니다 :

예 :

template < typename... Args > 
struct zipper : zipper < typename process_zipper_arguments <Args...>::type > {}; 
  • 가 추적하는 template < typename... Args > struct typelist {}를 사용 상속 할 오브젝트 유형.
  • 위해

중복 부모 유형을 제거 할 수있는 실제 상속을 할 struct zipper < typelist <Args...> >: public virtual Args... 전문은,이 개 도우미 기능 process_zipper_arguments에 사용됩니다

  • is_in < CandidateType, typelist<Args...> >::typetrue_type 또는 false_type 중 하나이며 정의 할 수 있습니다 재귀 적으로
  • add_unique < CandidateType, typelist<Args...> >::typetypelist <...>이며 CandidateType이 추가되거나 추가되지 않습니다. 그것을 결정하기 위해 is_in이 호출됩니다.

다음은 적어도 g ++ (GCC) 4.6.3과 --std = C++ 0x로 컴파일 된 완전한 코드입니다. 그것에 대한 비판은 환영합니다.

// Forward declarations 
template < typename... Args > 
struct zipper; 

// Two types meaning true and false 
struct true_type {}; 
struct false_type {}; 

// The only purpose of this struct is to be associated with Types... 
template < typename... Types > 
struct typelist {}; 


// =================================================== 
// is_in < type, typelist<...> >::type 
//  is true_type if type is in typelist 
//  is false_type if type is not in typelist 

// Assume TElement is not in the list unless proven otherwise 
template < typename TElement, typename TList > 
struct is_in { 
    typedef false_type type; 
}; 

// If it matches the first type, it is definitely in the list 
template < typename TElement, typename... TTail > 
struct is_in < TElement, typelist < TElement, TTail... > > 
{ 
    typedef true_type type; 
}; 

// If it is not the first element, check the remaining list 
template < typename TElement, typename THead, typename... TTail > 
struct is_in < TElement, typelist < THead, TTail... > > 
{ 
    typedef typename is_in < TElement, typelist <TTail...> >::type type; 
}; 

// =================================================== 
// add_unique < TNew, typelist<...> >::type 
//  is typelist < TNew, ... > if TNew is not already in the list 
//  is typelist <...> otherwise 

// Append a type to a type_list unless it already exists 
template < typename TNew, typename TList, 
    typename Tis_duplicate = typename is_in < TNew, TList >::type 
    > 
struct add_unique; 

// If TNew is in the list, return the list unmodified 
template < typename TNew, typename... TList > 
struct add_unique < TNew, typelist <TList...>, true_type > 
{ 
    typedef typelist <TList...> type; 
}; 

// If TNew is not in the list, append it 
template < typename TNew, typename... TList > 
struct add_unique < TNew, typelist <TList...>, false_type > 
{ 
    typedef typelist < TNew, TList... > type; 
}; 

// =================================================== 
// process_zipper_arguments <Args...>::type 
//  returns a typelist of types to be inherited from. 
// 
// It performs the following actions: 
// a) Unpack zipper<...> and typelist <...> arguments 
// b) Ignore values that are already in the list 

template < typename... Args > 
struct process_zipper_arguments; 

// Unpack a zipper in the first argument 
template < typename... ZipperArgs, typename... Args > 
struct process_zipper_arguments < zipper <ZipperArgs...>, Args... > 
{ 
    typedef typename process_zipper_arguments < ZipperArgs..., Args... >::type type; 
}; 

// Unpack a typelist in the first argument 
template < typename... TypeListArgs, typename... Args > 
struct process_zipper_arguments < typelist <TypeListArgs...>, Args... > 
{ 
    typedef typename process_zipper_arguments < TypeListArgs..., Args... >::type type; 
}; 

// End the recursion if the list is empty 
template < > 
struct process_zipper_arguments < > 
{ 
    typedef typelist < > type; 
}; 

// Construct the list of unique types by appending them one by one 
template < typename THead, typename... TTail > 
struct process_zipper_arguments < THead, TTail... > 
{ 
    typedef typename 
    add_unique < THead, 
     typename process_zipper_arguments <TTail...>::type 
    >::type type; 
}; 


// =================================================== 
// The zipper class that you might want 


// If the list of types is not yet known, process it. 
// The inheritance is ugly, but there is a workaround 
template < typename... Args > 
struct zipper : zipper < typename process_zipper_arguments <Args...>::type > 
{ 
    // // Instead of inheriting, you can use zipper as a factory. 
    // // So this: 
    // typedef zipper < meas2, zipper < meas1, meas > > mymeas; 
    // // Turns to: 
    // typedef typename zipper < meas2, zipper < meas1, meas > >::type mymeas; 
    typedef zipper < typename process_zipper_arguments <Args...>::type > type; 
}; 

// If the list of types is known, inherit from each type 
template < typename... Args > 
struct zipper < typelist <Args...> > 
: public virtual Args... 
{}; 

// =================================================== 
// Short usage demo, replace with your own code 

struct meas { 
    int i; 
}; 

struct meas2 { 
    int j; 
}; 

struct meas3 { 
    int k; 
}; 


typedef zipper < meas, meas, meas3 > meas_type; 
typedef zipper < meas2, meas_type, meas2 > meas_type2; 

typedef typename zipper <meas_type2>::type nicer_meas_type2; 


int main (int, char**) 
{ 
    meas * m = new meas_type2; 
    meas_type2 n; 
    nicer_meas_type2 o; 

    return 0; 
} 

가합니다 (return 0; 줄에 중단 점) 결과 다음을 제공합니다 디버깅 :

(gdb) print *m 
$1 = {i = 0} 
(gdb) print n 
$2 = {<zipper<typelist<meas, meas3, meas2> >> = {<meas> = {i = 4196320}, <meas3> = {k = 0}, <meas2> = {j = 0}, 
    _vptr.zipper = 0x400928}, <No data fields>} 
(gdb) print o 
$3 = {<meas> = {i = 4195719}, <meas3> = {k = 0}, <meas2> = {j = 1}, _vptr.zipper = 0x4009a8 <VTT for zipper<typelist<meas, meas3, meas2> >>} 
+0

이것은 서사시이며 정확하게 내가 찾고 있던 것입니다! 템플릿 foo는 매우 강합니다 :) –

+0

고마워요, 솔직히 말해서, 저는 일주일 전에 아주 비슷한 문제에 직면했는데, 시간이 좀 걸렸습니다. 그래서 저는 여전히 내 머리 속에 새 것이 었습니다. – mars