2014-04-17 2 views
4

SFINAE을 사용하면 가변 클래스 클래스 템플릿의 개별 요소에 액세스 할 수 있습니다. 내 문제는 기본 클래스가 variadic 클래스 템플릿을 상속 한 다음 파생 클래스가 기본 템플릿과 다른 템플릿 인수를 사용하여 variadic 클래스 템플릿을 상속 할 때 발생합니다. 상속 체인을 따라야하는 모호성이 있습니다. 모호성을 해결할 방법이 있습니까? 예를 들어기본 클래스와 파생 클래스 모두 가변 상속 체인이있는 경우 모호한 SFINAE 액세스가 발생하지 않음

는 :

// compile with flag: -std=c++11 
#include <type_traits> 

struct A { int x; }; 
struct B { int x; }; 
struct C { int x; }; 
struct D { int x; }; 

template <class ... Params> class Parameter { }; 

template <class Param, class ... Tail> 
class Parameter<Param, Tail ...> : public Param, public Parameter<Tail ...> 
{ 
    public: 
    //! Get a parameter 
    template <class Param2> 
    typename std::enable_if<std::is_same<Param, Param2>::value, int>::type 
    getParam() const 
    { return Param::x; } 

    //! Get a parameter. Delegate false template matches down the Tail... inheritance line (see SFINAE) 
    template <class Param2> 
    typename std::enable_if<! std::is_same<Param, Param2>::value, int>::type 
    getParam() const 
    { return Parameter<Tail ...>::template getParam<Param2>(); } 
}; 

class Base : public Parameter<A, B> 
{ }; 

class Derived : public Base, public Parameter<C, D> 
{ }; 

int main(int const argc, char const * argv[]) 
{ 
    Base base; 
    int a = base.getParam<A>(); // ok 
    int b = base.getParam<B>(); // ok 

    Derived derived; 
    int c0 = derived.getParam<C>(); // error: request for member ‘getParam’ is ambiguous 
    int c1 = derived.Derived::getParam<C>(); // error: request for member ‘getParam’ is ambiguous 
    int c2 = derived.Parameter<C, D>::getParam<C>(); // ok but syntax overly complex, especially if many params 

    int a0 = derived.getParam<A>(); // error: request for member ‘getParam’ is ambiguous 
    int a1 = derived.Base::getParam<A>(); // ok, could be acceptable if also worked on Derived 
    int a2 = derived.Parameter<A, B>::getParam<A>(); // ok but syntax overly complex and confusing 

    return 0; 
} 

나는 몇 가지 주요 설계 변경 문제를 해결할 수 있음을 깨닫게 : 1) 한 번에 2에서 각 매개 변수 중 하나에서 파생의 가변 매개 변수 클래스 템플릿을 제거하는 대신 얻을)를 만들 매개 변수 멤버 변수. 하지만 내 질문은 variadic Parameter 클래스 템플릿을 유지하면서 모호성을 피할 수 있는지 여부입니다.

+2

, 나는이 같은 것을 작성합니다 https://ideone.com/BbLKxb – Jarod42

+1

@ Jarod42 이것은을 코멘트가 아니라, 내가 생각하는 대답이다! – Sheljohn

+0

@ jarod42 단일 상속의 경우 약간의 동작 변화 : 만약 우리가'Parameter '이라면 위의 코드는 모호하다. OP가 신경 쓰면 패치 할 수 있지만, OP는이 경우 오류를 원할 수도 있습니다. – Yakk

답변

3

나는 다음과 모호성을 피하기 위해 일치하지 않습니다 getParam 제거하는 것입니다 : 개인적으로 Demo

template <class ... Params> class Parameter; 

template <class Param> 
class Parameter<Param> : public Param 
{ 
public: 
    //! Get a parameter 
    template <class Param2> 
    typename std::enable_if<std::is_same<Param, Param2>::value, int>::type 
    getParam() const 
    { return Param::x; } 
}; 

template <class Param, class ... Tail> 
class Parameter<Param, Tail ...> : public Parameter<Param>, Parameter<Tail...> 
{ 
public: 
    using Parameter<Param>::getParam; 
    using Parameter<Tail...>::getParam; 
}; 

class Base : public Parameter<A, B> {}; 

class Derived : public Base, public Parameter<C, D> 
{ 
public: 
    using Base::getParam; 
    using Parameter<C, D>::getParam; 
}; 
+0

매우 유용합니다. 그러나 매개 변수 :: getParam;을 사용하면 사용자가 작성하기가 쉽지 않습니다. 위의 'public Parameter '과 동기화 된 구문을 유도해야하므로 파생 된 부분을 처리하고 추가합니다 더 많은 매개 변수가 있습니다. 'Parameter '가 두 곳에서 언급되는 것을 피하는 방법은 없습니까? –

+0

'Derived '가'Base'를 파생하지 않고'C'와'D'까지 확장하고 싶다면 extend = Parameter 를 추가 할 수 있습니다. >;'클래스'Parameter' 내부에'클래스 Derived : public Base :: extend '이 있습니다. – Jarod42

+0

나는 빈 매개 변수 <...> 클래스를 사용하여 마치 매개 변수를 떼어 낸 다음 매개 변수 이름 앞에 접두사가 붙은 일반 멤버 함수 (예 :'derived.Parameter :: get()')를 호출합니다. 그래서 나는 당신의 대답을 받아들이지 만, 나의 결론은 여전히 ​​두 개의 가변성 (variadic) 템플릿 상속 체인 사이의 애매함이 불가피하다는 것이다. –