2017-01-19 45 views
3

我想提供取決於是否與給定類型的operator>>的兩種不同的實現是一種特殊類型的子類:的std :: is_base_of爲模板向量函數參數

class A {}; 

class B : public A{}; 
class C {}; 

template<typename T> 
std::istream& operator>>(std::istream& is, 
    std::vector<typename std::enable_if<std::is_base_of<A, T>::value>::type>& vec) 
{ 
    std::cout << "Special case called" << std::endl; 
    return is; 
} 

template<typename T> 
std::istream& operator>>(std::istream& is, 
    std::vector<T>& vec) 
{ 
    std::cout << "General case called" << std::endl; 
    return is; 
} 


void main(int argc, char **argv) 
{ 
    std::vector<A> a; 
    std::vector<B> b; 
    std::vector<C> c; 
    std::stringstream ss("A string"); 
    ss >> a; 
    ss >> b; 
    ss >> c; 
} 

它打印

General case called 
General case called 
General case called 

改變第二運營商定義

template<typename T> 
std::istream& operator>>(std::istream& is, 
    std::vector<typename std::enable_if<!std::is_base_of<A, T>::value>::type>& vec) 
{ 
    std::cout << "General case called" << std::endl; 
    return is; 
} 

不因爲

error C2678: binary '>>' : no operator found which takes a left-hand operand of type 'std::stringstream' 

所以我可能用錯std::enable_if無法編譯。但是什麼是正確的?這裏的模板化std::vector有問題嗎?

回答

8

我不認爲的std :: enable_if是在最需要的位置在這裏,我會把它的返回類型,使SFINAE:正確

template<typename T> 
typename std::enable_if<std::is_base_of<A, T>::value,std::istream>::type& 
operator>>(std::istream& is,std::vector<T>& vec) 
{ 
    std::cout << "Special case called" << std::endl; 
    return is; 
} 

template<typename T> 
typename std::enable_if<!std::is_base_of<A, T>::value,std::istream>::type& 
operator>>(std::istream& is,std::vector<T>& vec) 
{ 
    std::cout << "General case called" << std::endl; 
    return is; 
} 

live demo

+0

非常感謝,這解決了問題!我有一個簡短的後續問題:我可以提供第三個明確的專業化嗎?''template <> std :: istream&operator >>(std :: istream&is,std :: vector &vec){...}''不會編譯,因爲錯誤C2794:'type':不是任何直接或間接基類的成員'std :: enable_if ''' – PhilLab

+3

@PhilLab傾向於超過函數特化 –

+0

duuhhh,我錯過了顯而易見的:-)。如果你只有一把錘子,一切看起來都像釘子 – PhilLab

2

@ Biggy的答案演示了一種正確執行此操作的方法。下面是我找到更多的可讀性(有一些小的修正和改進沿)的替代:

template<typename T, typename std::enable_if<std::is_base_of<A, T>{}, int>::type = 0> 
std::istream& operator >>(std::istream& is, std::vector<T>& vec) { 
    std::cout << "Special case called\n"; 
    return is; 
} 

template<typename T, typename std::enable_if<!std::is_base_of<A, T>{}, int>::type = 0> 
std::istream& operator >>(std::istream& is, std::vector<T>& vec) { 
    std::cout << "General case called\n"; 
    return is; 
} 

Online Demo

或者更好的是,使用標籤調度:

template<typename T> 
std::istream& impl(std::istream& is, std::vector<T>& vec, std::true_type) { 
    std::cout << "Special case called\n"; 
    return is; 
} 

template<typename T> 
std::istream& impl(std::istream& is, std::vector<T>& vec, std::false_type) { 
    std::cout << "General case called\n"; 
    return is; 
} 

template<typename T> 
std::istream& operator >>(std::istream& is, std::vector<T>& vec) { 
    return impl(is, vec, std::is_base_of<A, T>{}); 
} 

Online Demo

在實踐中,我發現當條件數量很大時,後一種方法更容易維護,並導致更容易被破譯的錯誤消息。

至於爲什麼你代碼不起作用,問題是std::enable_if<std::is_base_of<A, T>::value>::type作爲參數類型不是T一個抵扣範圍內,並有涉及到T扣除助手沒有其他參數。所以,因爲您在撥打電話時沒有明確指定T,所以運營商根本就不是可以調用的合格候選人,因爲T未知。當你在其他的過載中使用相同的嘗試SFINAE時,兩個過載都是可行的,你會得到你的錯誤。一個寬鬆的經驗法則是:如果您需要typename(與enable_if的用法一樣),您不再處於可推論的環境中。

enable_if置於返回類型或作爲默認模板參數可以工作,因爲參數類型(特別是nice和簡單的std::vector<T>&)可以推出T

+0

謝謝,尤其是對於爲什麼它不起作用的解釋 – PhilLab