5

나는 다음 클래스를 가지고 기능이 shared_ptr<T>C++ (11) 방법 템플릿 특수화는

Foo foo; 
foo.bar<shared_ptr<int>>(); 
foo.bar<shared_ptr<long>>(); 

으로 그러나 물론 호출 될 때 경우에 나는 전체 스펙을 작성하지 않으 각 유형에 대한 초기화. 그러한 행동을 구현할 수 있습니까? (필요한 경우 형질 기반이 될 수 있습니까?)

+2

기능 템플릿은 부분적으로 특수화 할 수 없지만 클래스 템플릿은 할 수 있습니다. 공용 정적 멤버 함수를 노출하고 부분적으로 클래스를 특수화하고 멤버 함수를 적절하게 변경하는 래핑 클래스 템플릿을 만듭니다. – 0x499602D2

답변

4

이 하나가 T 구분하는 SFINAE을 사용할 수 있습니다 및 std::shared_ptr<U> :

template <typename T> 
struct is_shared_ptr_impl : std::false_type {}; 
template <typename T> 
struct is_shared_ptr_impl<std::shared_ptr<T>> : std::true_type {}; 
template <typename T> 
using is_shared_ptr = typename is_shared_ptr_impl<typename std::decay<T>::type>::type; 

class Foo 
{ 
public:  
    template <typename T> 
    auto bar() 
     -> typename std::enable_if<!is_shared_ptr<T>{}, T>::type 
    { 
     std::cout << "T is " << typeid(T).name() << std::endl; 
     return {}; 
    } 

    template <typename T> 
    auto bar() 
     -> typename std::enable_if<is_shared_ptr<T>{}, T>::type 
    { 
     using U = typename std::decay<T>::type::element_type; 
     std::cout << "T is shared_ptr of " << typeid(U).name() << std::endl; 
     return {}; 
    } 
}; 

DEMO

+0

cv 자격을 고려하지 않았습니다. 'is_shared_ptr''is_shared_ptr_impl'과'is_shared_ptr'을 별칭 템플릿으로 만드는 것은'is_shared_ptr_impl :: type>'을 참조합니까? – Columbo

+0

@Columbo 어때? –

+0

나는 참고 문헌을 생각하지 않았다. 이제 괜찮아. – Columbo

6

확실히 여러 가지 방법이 있습니다. 내 마음에 오는 첫 번째 방법은 단순히 함수 오버로딩입니다. 오버로드 할 매개 변수가 없으므로 매개 변수를 만들어야합니다. 포인터를 좋아하는데, 이는 함수에 유형을 전달하는 방법으로 효과적으로 작용합니다.

class Foo { 

    //regular overload 
    template<typename T> 
    T bar(T*) { //takes a pointer with an NULL value 
     cout << "Called with return type: " << typeid(T).name() << endl; 

     T t = //... (some implementation here) 

     return t; 
    } 
    //shared_ptr overload - NOTE THAT T IS THE POINTEE, NOT THE SHARED_PTR 
    template<typename T> 
    std::shared_ptr<T> bar(std::shared_ptr<T>*) { //takes a pointer with an null value 
     cout << "Called with return type: " << typeid(T).name() << endl; 

     std::shared_ptr<T> t = //... (some implementation here) 

     return t; 
    } 

public: 
    template <typename T> 
    T bar() { 
     T* overloadable_pointer = 0; 
     return bar(overloadable_pointer); 
    } 
}; 

나는 주위 종류를 전달하는 포인터를 사용하여 다른 사람이 들어, 그래서이 작업을 수행하도록 선택하는 경우, 안전을 위해 철저하게 언급 없어요. wierd 코드입니다.

템플릿 기능을 수행하기 위해 단순히 도우미 구조체를 사용하는 것이 더 직관적 일 수 있습니다. 이는 대부분의 사람들이하는 일입니다. 불행하게도 Foo (아마도 그렇게 할 것임) 멤버에게 액세스해야하는 경우 템플릿 전문화를 사용하려면 모든 멤버를 함수에 전달하거나 템플릿 도우미를 친구로 초대해야합니다. 양자 택일로, 당신은 다른 멤버에게 type_traits 전문화를 전달할 수 있지만, 단순히 위의 포인터 트릭의 복잡한 버전이됩니다. 많은 사람들이 더 정상적이고 덜 혼동 스럽다는 것을 알게되었으므로 다음과 같습니다.

template<typename T> 
struct Foo_tag {}; 

class Foo { 
    //regular overload 
    template<typename T> 
    T bar(Foo_tag<T>) { 
    } 
    //shared_ptr overload - NOTE THAT T IS THE POINTEE, NOT THE SHARED_PTR 
    template<typename T> 
    std::shared_ptr<T> bar(Foo_tag<std::shared_ptr<T>>) { 
    } 

public: 
    template <typename T> 
    T bar() { 
     return bar(Foo_tag<T>{}); 
    } 
} 
+1

재미있는 방법입니다. – 0x499602D2

+0

실제로, 나는 그것에게 시도를 줄 것이다 :) – Daimon

+0

왜 "정의되지 않은 값"? 값은 0 ... 당신은 그 이름이 바르지 않다는 것을 의미합니까? – Barry

8

기능을 부분적으로 특수화 할 수 없습니다. 이유에 대한 이야기는 GOTW에서 확인하십시오.

당신 부분적으로하지만 클래스를 전문으로 할 수 있습니다, 그래서 당신은 무엇을 할 수 있는지입니다 :

template <typename T> 
T bar() { 
    return bar_impl<T>::apply(this); 
} 

여기서 아무도 아직 그것을 제안하기 때문에

template <typename T> 
struct bar_impl { 
    static T apply(Foo*) { 
     // whatever 
    } 
} 

template <typename T> 
struct bar_impl<std::shared_ptr<T>> { 
    static std::shared_ptr<T> apply(Foo*) { 
     // whatever else 
    } 
} 
+0

0x499602D2 코멘트 후에 내 마음에 온 해결책이 될 것 같습니다. 또한 Mooing Duck의 대답보다 조금 덜 "해킹 된"것처럼 보입니다 – Daimon

+0

@Daimon 필자는 필연적으로 "hacky"라고 부를 것입니다. 나는 그것을 본 적이 한번도 없다 - 그리고 그것은 적어도 두 가지 장점을 제공한다. (1) 모든 Foo 코드가 Foo에있다. (2) Foo의 private 멤버에게 우정없이 접근 할 수있다. 내 것은 이것을 더 많이 받아 들일 수있는 일반적인 방법 일뿐입니다. – Barry

+0

우정에 관한. bar_impl을 Foo의 내부 클래스로 만들 수 없습니까? 이것은 우정을 자동으로 만들어야하며 내부적으로 구현을 유지해야합니까? – Daimon