2010-04-27 6 views
5

question에 대한 대답을 읽은 후 SFINAE를 사용하여 클래스에 특정 구성원 기능이 있는지 여부에 따라 두 가지 기능 중 하나를 선택할 수 있음을 알게되었습니다. 여러 작업을 수행 할 수 SFINAE을 확장 할 수 있다면 궁금여러 SFINAE 규칙

template<typename T> 
void Func(T &arg, int_to_type<true>); //T has X() 

template<typename T> 
void Func(T &arg, int_to_type<false>); //T does not have X() 

된다

template<typename T> 
void Func(T& arg) 
{ 
    if(HAS_MEMBER_FUNCTION_X(T)) 
     arg.X(); 
    else 
     //Do something else because T doesn't have X() 
} 

: 그것은 if 문에서 각 지점이 오버로드 된 함수로 분할 그냥 다음에 해당하는의 규칙. 다음과 같은 내용이 될 수 있습니다.

template<typename T> 
void Func(T& arg) 
{ 
    if(HAS_MEMBER_FUNCTION_X(T))    //See if T has a member function X 
     arg.X(); 
    else if(POINTER_DERIVED_FROM_CLASS_A(T)) //See if T is a pointer to a class derived from class A 
     arg->A_Function();    
    else if(DERIVED_FROM_CLASS_B(T))   //See if T derives from class B 
     arg.B_Function(); 
    else if(IS_TEMPLATE_CLASS_C(T))    //See if T is class C<U> where U could be anything 
     arg.C_Function(); 
    else if(IS_POD(T))       //See if T is a POD type 
     //Do something with a POD type 
    else 
     //Do something else because none of the above rules apply 
} 

이렇게 할 수 있나요?

감사합니다.

답변

6

확실히 가능합니다. 모든 지점이 상호 배타적임을 보장하기 위해 조심해야합니다. 그렇지 않으면 애매하게 될 것입니다.

이것을 지원하는 데 가장 적합한 두 가지 도구 인 Boost Type TraitsBoost Enable If을 살펴보십시오. Boost ICE (Integral Constant Expression의 약자)은 여러 유형 특성을 결합하여보다 복잡한 유형 일치를 수행하고 과부하가 상호 배타적임을 확인하는 데 사용할 수 있습니다.

다소 복잡하고 복잡하므로 . 여기에 상대적으로 간단한 예제에서는 클래스 계층 구조가 있다고 가정입니다 :

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

과 하나 Base하는 기능 foo의 과부하 및 Base에서 파생 된 모든 클래스에 대한 또 다른 오버로드를 호출하려는 첫 번째 시도는 것처럼 보일 수 있습니다. :

#include <boost/type_traits.hpp> 
#include <boost/utility/enable_if.hpp> 

using namespace boost; 
using namespace boost::type_traits; 

template <typename T> 
typename enable_if<is_same<Base, T>, void>::type 
foo(const T&) { } 

template <typename T> 
typename enable_if<is_base_of<Base, T>, void>::type 
foo(const T&) { } 

그러나 foo(Base())를 호출을 시도 그래서 만약 T는, 기본 클래스의 경우 is_base_of true를 반환, 두 기능 템플릿 매치 때문에 모호함이있다.

template <typename T> 
typename enable_if<is_same<Base, T>, void>::type 
foo(const T&) { } 

template <typename T> 
typename enable_if< 
    ice_and< 
     is_base_of<Base, T>::value, 
     ice_not<is_same<Base, T>::value>::value 
    >, void>::type 
foo(const T&) { } 

이 오버로드는 상호 배타적이며, 그들은 어떤 모호함이없는 보장 : 우리는 부스트 ICE 도우미를 유형 특성의 조합을 사용하고 사용하여이 문제를 해결할 수 있습니다.

예제 중 일부는 지원되지 않습니다 (즉, HAS_MEMBER_FUNCTION_X, 어떤 작업을 할 수 있는지에 따라 IS_TEMPLATE_CLASS_C에 대해 확신 할 수는 없지만 일반적으로 가능함). .

+0

감사합니다 (호출 할 때 분명히, 당신은 옵션은 상호 배타적이지로 조심해야한다). 나는 IS_TEMPLATE_CLASS_C 예제를 제외하고는 enable_if를 사용하여 모든 것을 작동시킬 수 있었다. is_base_of와 비슷한 것을 쓸 수있는 방법이 있습니까? T는 등 C , C ,, 그리고 다른 모든 – Fred

+0

@Fred에 대한 잘못된 경우 즉 < C, T > :: 값 is_template_of 사실이 될 것이다 : 문제는'C '와'C '입니다 관련이없는 유형이다. 저의 첫 번째 생각은 클래스 템플릿'C' (IsInstantiatedTemplateC')에 typedef 또는 정수형 멤버를 추가하고 그 존재를 테스트하는 것입니다. 또는 빈 클래스'BaseC'에서 공개적으로 클래스 템플릿'C'를 파생시키고 해당 유형의 상속을 테스트 할 수 있습니다. 유형이 일부 템플릿의 인스턴스 생성인지 여부를 알고 싶다면 다소 이상한 일입니다. 일반적으로 타입이 알려진 인터페이스를 구현하는 한 상관하지 않습니다. –

1

구현 방법은 없습니다. arg에 함수 중 하나가 없으면 컴파일이 실패합니다. (나는 이것을 잘 알고 있다고 생각한다.)

그러나 템플릿 특수화 (boost mpl의 마법으로 숨김)를 사용하여 그렇게 할 수 있습니다.

당신이 언젠가 메타 기능을 사용하여이 부스트 MPL 벡터를 할 수있는 : 정확히 요구 사항에 따라 http://www.boost.org/doc/libs/1_40_0/libs/mpl/doc/refmanual.html

typedefs typename mpl::vector<f0,f1,...>::type handlers; // different handlers 
// convert logic to int N to map condition to handler 
// can use ternary or bit shift trick 
// more general approach could be to use vector of mpl::bool_ and mpl::find 

typedef typename mpl::vector_c<bool, (first_condition), 
            (second_condition),...>::type condition; 

typedef typename mpl::find<condition, mpl:: bool_<true> >::type iterator; 
typedef typename mpl::at<handlers, iterator::pos::value>::type handler; 
handler::apply(...); // call handler with some arguments 

체크 아웃, 당신은 다른 접근 방법을 시도 할 수 있습니다. 당신이

if (a) { X(); } 
else if (b) { Y(); } 

이 동일하지만

if (a) { X(); } 
if (!a && b) { Y(); } 

, 당신은 또한 당신의 true/false 이분법을 확장 할 수 있음을 의미 깨닫게 때 위 뭔가 짓입니다 몇 시간 전에

+1

두 번째 예제의 전체적인 점은 사실 우리가 원하는 것을 보여주기위한 것입니다. 처음 컴파일하지 않은 것처럼 컴파일되지 않습니다. 그러나 거기에서 그는 대안을 제시했다. – MSalters

1

질문은 간단합니다.

enum FuncVariants { HasMember, PointerDerivedFromA, DerivedFromB, InstanceOfC, isPod } 
template<typename T> 
void Func(T &arg, int_to_type<HasMember>); 

template<typename T> 
void Func(T &arg, int_to_type<DerivedFromA>); 

template<typename T> 
void Func(T &arg, int_to_type<DerivedFromB>); 

template<typename T> 
void Func(T &arg, int_to_type<InstanceOfC>); 

는 제안 제임스에 대한