2014-11-13 90 views
5

我有以下類:C++ 11方法的返回類型的模板特

class Foo { 
public: 
    template <typename T> 
    T bar() { 
     cout << "Called with return type: " << typeid(T).name() << endl; 

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

     return t; 
    } 
} 

它在以下方式調用:

Foo foo; 
int i = foo.bar<int>(); 
long l = foo.bar<long>(); 

現在我想有不同的專業化情況下,當函數與shared_ptr<T>

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

但當然調用我不想創建完整規格每種類型的ialization。是否有可能實現這種行爲(如果需要,可以基於特徵)?

+2

函數模板不能部分專用,但類模板可以。創建一個公開靜態成員函數的包裝類模板,部分專門化該類並適當地更改成員函數。 – 0x499602D2

回答

4

由於沒有人提出它,人們可以使用SFINAE T區分和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

你不考慮簡歷資格。如何使'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); 
    } 
}; 

我從來沒有聽說過其他任何人使用指針是傳遞類型的,所以如果你選擇這樣做,充分發表意見,只是爲了安全起見。它奇怪的代碼。

簡單地使用助手結構來完成模板特化可能更直觀,這是大多數人會做的。不幸的是,如果您需要訪問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我不知道我會把它稱爲「哈克」必然。我從來沒有見過它 - 它確實提供了至少兩個優勢,超過我的答案:(1)所有'Foo'代碼都在'Foo'中(2)您可以訪問沒有友誼的私人成員'Foo'。礦是這樣做的更加接受/常見的方式。 – Barry

+0

關於友誼。我不能讓bar_impl成爲Foo的內部類嗎?這應該自動創造友誼,並仍然在內部保持實施? – Daimon

相關問題