2015-01-30 9 views
7

선언에 SFINAE (또는 다른 기술)을 사용하여 템플릿 클래스에서 전용을 파생시킬 수 있습니까? (가능이있는 경우)'using'선언을 SFINAE로 사용

#include <iostream> 

struct S1 { 
    void f() { std::cout << "S1::f\n"; } 
}; 

struct S2 { 
    void f() { std::cout << "S2::f\n"; } 
    void g() { std::cout << "S2::g\n"; } 
}; 

template <class T> 
struct D : private T { 
    using T::f; 
    // using T::g; // need this only if T provides g() function 
}; 

int main() { 
    D<S1>().f(); // ok. Prints 'S1::f' 
    D<S2>().f(); // ok. Prints 'S2::f' 
    D<S2>().g(); // fail. But wants to be ok and prints 'S2::g' 
    return 0; 
} 

가 어떻게 원하는 동작에 도달 할 수 있습니다 : 더 나은 이해를 위해 코드 아래를 참조하십시오?

+0

가능한 복제본 [함수의 존재를 확인하기 위해 C++ 템플릿을 작성할 수 있습니까?] (http://stackoverflow.com/questions/257288/is-it-possible-to-write-ac-template- 기능 확인 - 존재 여부) – Klaus

+2

@Klaus 아니요, 이것은 중복이 아닙니다. 이 질문은 쉽게 우리가 이미 has_function_named_g '이라는 특성을 가지고 있다고 가정 할 수 있습니다. 이제 뭐? 'using' 선언에 어떻게 적용합니까? – Angew

답변

4

C++ 부분 템플릿 특수화 및

#include <iostream> 
#include <type_traits> 

struct S1 { 
    void f() { std::cout << "S1::f\n"; } 
}; 

struct S2 { 
    void f() { std::cout << "S2::f\n"; } 
    void g() { std::cout << "S2::g\n"; } 
}; 

template <class T, class V = void> 
struct D : private T { 
    using T::f; 
}; 

template <class T> 
struct D<T, decltype(void(&T::g))> : private T { 
    using T::f; 
    using T::g; // need this only if T provides g() function 
}; 

int main() { 
    D<S1>().f(); // ok. Prints 'S1::f' 
    D<S2>().f(); // ok. Prints 'S2::f' 
    D<S2>().g(); // ok. Prints 'S2::g' 
    return 0; 
} 
SFINAE

에 대한 decltype(void(&T::g))를 사용

Live Demo


편집 :

이보다 유연 또 다른 방법은,하지만 난 어떻게하는지 몰라 private virtual 상속은 실제 사용 사례에서 작동합니다. 문제가 발생할 수 있는지 알려주세요 (예 : UB).

이보다 보이지만, 여러 검사로 확장 할 쉽게 만들고, D<type-with-f>D<type-without-f> 사이에 공유되는 코드를 복제 필요하지 않습니다 브라이언 첸의 대답의 변형, 사용하는 것입니다
#include <iostream> 
#include <type_traits> 

struct S1 { 
    void f() { std::cout << "S1::f\n"; } 
}; 

struct S2 { 
    void f() { std::cout << "S2::f\n"; } 
    void g() { std::cout << "S2::g\n"; } 
}; 

struct S3 { 
    void g() { std::cout << "S3::g\n"; } 
}; 

template <class T, class = void> 
struct D_f {}; 

template <class T> 
struct D_f<T, decltype(void(&T::f))> : private virtual T { 
    using T::f; 
}; 

template <class T, class = void> 
struct D_g {}; 

template <class T> 
struct D_g<T, decltype(void(&T::g))> : private virtual T { 
    using T::g; 
}; 

template <class T> 
struct D : D_f<T>, D_g<T> { 
}; 


int main() { 
    D<S1>().f(); 
    D<S2>().f(); 
    D<S2>().g(); 
    D<S3>().g(); 
    return 0; 
} 

Live Demo

+1

그러나 이것은 잘 확장되지 않습니다. 사용할 수 있거나 사용하지 않을 수있는 n 개의 함수에 대해서는 2 ** n 클래스 정의를 기술해야합니다. – hvd

+0

@BryanChen 메인 D 클래스 템플릿의 변경을 제한 할 수 있습니까? 즉, T :: f;를 사용하여 코드의 특수화 및 복제를 추가하지 않고도 가능합니까? – alexolut

5

각 단계에서 하나의 추가 구성원을 확인하는 상속 체인입니다. 필요한 경우에만 생성자의 상속이 필요합니다.

struct A { 
    void f() { } 
    void g() { } 
    void i() { } 
}; 

// The generic case. D<T, char[N]> simply provides what D<T, char[N+1]> provides. 
template <typename T, typename U = char[1]> 
struct D : D<T, char[sizeof(U) + 1]> { 
    using D<T, char[sizeof(U) + 1]>::D; 
}; 

// The end of the chain. This is where T gets inherited. It declares all of its own 
// specialisations as its friends, so that they can access other members of T. 
template <typename T> 
struct D<T, char[6]> : private T { 
    template <typename, typename> 
    friend struct D; 

    D(int) { } 
    void fun() { } 
}; 

// Check for T::f. 
template <typename T> 
struct D<T, char[2 + !sizeof(&T::f)]> : D<T, char[3]> { 
    using D<T, char[3]>::D; 
    using T::f; 
}; 

// Check for T::g. 
template <typename T> 
struct D<T, char[3 + !sizeof(&T::g)]> : D<T, char[4]> { 
    using D<T, char[4]>::D; 
    using T::g; 
}; 

// Check for T::h. 
template <typename T> 
struct D<T, char[4 + !sizeof(&T::h)]> : D<T, char[5]> { 
    using D<T, char[5]>::D; 
    using T::h; 
}; 

// Check for T::i. 
template <typename T> 
struct D<T, char[5 + !sizeof(&T::i)]> : D<T, char[6]> { 
    using D<T, char[6]>::D; 
    using T::i; 
}; 

int main() { 
    D<A> d = 4; // ok: verify that constructors got inherited 
    // A &a = d; // error: verify that inheritance of A is private 
    d.f(); // ok: verify that f got inherited 
    d.g(); // ok: verify that g got inherited 
    // d.h(); // error: verify that h is not available 
    d.i(); // ok: verify that i got inherited 
    d.fun(); // ok: verify that the inheritance chain didn't get broken 
} 

참고 : 대신 &T::f을 확인, 당신은 대신 std::declval<T>().f()으로 뭔가를 할 수 있습니다. 전자는 오버로드 된 함수를 처리 할 수 ​​없습니다.

+0

흠, 나는'std :: integral_constant'를 사용했을지 모르지만, 아주 좋습니다. –

+0

@ T.C. ㅎ, 내가 알고있는 것 중 하나이지만, 유용 할 때마다 이미 필요하지 않은 해결책으로 이미 끝났기 때문에 사용하지 않게됩니다. :) – hvd