2012-05-15 2 views
6

이 문제는 설명하기가 좀 어렵다, 그래서 예를 들어 시작할 것이다 : 나는 유형과 템플릿 매개 변수로 정수 상수를 취하는 클래스 템플릿을템플릿 전문화

하고, 나는 그 템플릿의 인스턴스화에서 파생 자식 클래스의 수 있습니다

template <class V, int i> 
struct Base 
{ 
    static void doSomething() { cout << "something " << i << endl; }; 
}; 

struct Child : public Base<int,12> 
{ 
}; 

내가 다른 템플릿으로 이러한 클래스를 사용하려면 서로 다른 종류의 전문이있는, (의 테스트를 부르 자). Base의 인스턴스화에서 파생 된 모든 클래스에 대해 동작이 동일해야하므로 Base에서 파생 된 모든 클래스를 처리하는 Test의 단일 전문화 만 정의하려고합니다.

Base < V, i >에 직접 적용 할 수 없다는 것을 알고 있습니다. 하위 클래스를 감지하지 못하기 때문입니다. 대신, 내 첫 번째 방법은 부스트의 enable_if 및 유형 특성을 사용했다 :

// empty body to trigger compiler error for unsupported types 
template <class T, class Enabled = void> 
struct Test { }; 

// specialization for ints, 
// in my actual code, I have many more specializations here 
template <class Enabled> 
struct Test <int, Enabled> 
{ 
    static void test (int dst) 
    { 
     cout << "Test<int>::test(" << dst << ")" << endl; 
    } 
}; 

// this should handle all subclasses of Base, 
// but it doesn't compile 
template <class T, class V, int i> 
struct Test <T, typename enable_if <is_base_and_derived <Base <V,i>, T>>::type> 
{ 
    static void test (const T &dst) 
    { 
     dst.doSomething(); 
    } 
}; 

int main (int argc, char **argv) 
{ 
    Test <int>::test (23); 
    Test <Child>::test (Child()); 
    return 0; 
} 

아이디어는 전문화가 V와 I의 임의의 값으로 자료에서 파생 된 모든 클래스를 처리해야했다. 이 작동하지 않습니다, GCC는 불평 :

 
error: template parameters not used in partial specialization: 
error:   ‘V’ 
error:   ‘i’ 

나는 문제가이 방법은 V의 모든 가능한 조합을 시도하는 컴파일러를 필요로하고 그 중 하나가 일치하면 내가 확인하는 것입니다 같아요.

template <class V, int i> 
struct Base 
{ 
    typedef V VV; 
    static constexpr int ii = i; 
    static void doSomething() { cout << "something " << i << endl; }; 
}; 

이 방법은, 전문화가 더 이상 V가 있어야하고 난 무료 템플릿 매개 변수 :

template <class T> 
struct Test <T, typename enable_if <is_base_and_derived <Base <typename T::VV, T::ii>, T>>::type> 
{ 
    static void test (const T &dst) 
    { 
     dst.doSomething(); 
    } 
}; 

그리고 지금, 나는 기본 클래스에 뭔가를 추가하여 문제를 해결했다 그것은 컴파일됩니다.

자, 내 질문은 : 어떻게하면 기본 클래스를 수정하지 않고도이 작업을 수행 할 수 있습니까?이 경우 직접 작성했기 때문에 가능했지만 테스트 템플릿에서 제 3 자 라이브러리 코드를 처리해야한다면 어떻게해야합니까? 보다 우아한 솔루션이 있습니까?

편집 : 또한 누군가가 나에게 왜 정확하게 첫 번째 접근 방식이 작동하지 않는지에 대한 자세한 설명을 줄 수 있습니까? 나는 대략적인 생각을 가지고 있지만 적절한 이해를 원합니다. :-)

간단한 해결책이 Base을 수 있도록하는 것입니다

답변

3

다른 Base_base 상속 :

template <class V, int i> 
struct Base3rdparty 
{ 
    static void doSomething() { cout << "something " << i << endl; }; 
}; 

template <class V, int i> 
struct Base 
: public Base3rdparty<V, i> 
{ 
    typedef V VV; 
    static constexpr int ii = i; 
}; 

template <class T> 
struct Test <T, typename enable_if <is_base_and_derived <Base <typename T::VV, T::ii>, T>>::type> 
{ 
    static void test (const T &dst) 
    { 
     dst.doSomething(); 
    } 
}; 
+0

Base_Base 팁을 주셔서 감사합니다. 현재 코드를 좀 더 읽기 쉽게 만들어줍니다. 그러나 제 3 자 라이브러리의 경우 하위 클래스가 라이브러리에도 속해있는 경우에는 작동하지 않습니다. –

+1

@BenjaminSchug : 아마도 [this] (http://stackoverflow.com/a/6398983/1324131)는 편집 된 질문에서 새 질문에 대한 답변을 제공합니다. – user2k5

+0

감사합니다. 첫 번째 방법이 효과가 없었던 이유입니다.내 직감이 맞는 것처럼 보입니다. –

0

:

struct Base_base 
{}; 

template <class V, int i> 
struct Base 
: public Base_base 
{ 
    static void doSomething() { cout << "something " << i << endl; }; 
}; 

template <class T> 
struct Test <T, typename enable_if <is_base_and_derived <Base_base, T>>::type> 
{ 
    static void test (const T &dst) 
    { 
     dst.doSomething(); 
    } 
}; 

은 [ 는 편집이] 제 3 자 코드에, 당신은 같은 트릭을 사용할 수 있습니다 함수 오버로딩을 사용하고 decltype :

// Never defined: 
template<typename T> std::false_type is_Base(T&); 
template<class V, int I> std::true_type is_Base(Base<V,I>&); 

template<typename IsBase, typename T> struct TestHelper; 
template<typename T> struct TestHelper<std::true_type, T> 
{ 
    static void test(const T& dst) { dst.doSomething(); } 
}; 
template<> struct TestHelper<std::false_type, int> 
{ 
    static void test(int dst) 
    { std::cout << "Test<int>::test(" << dst << ")" << std::endl; } 
}; 
// ... 

template<typename T> struct Test 
{ 
    static void test(const T& dst) 
    { TestHelper<decltype(is_Base(std::declval<T&>())), T>::test(dst); } 
}