2016-09-15 73 views
3

我設置了兩個模板函數,它們爲不同的STL容器獲得總和:一個用於列表和矢量,另一個用於地圖。函數模板重載vs顯式特化

請參閱第二個模板函數定義的註釋行(1)和(2)。註釋掉的代碼(= 2)也可以正常工作,所以我不知道哪一個是更受推薦的語法。

另外,每種方法如何調用(我在我的評論中恰當地猜到了)?說(1)是一個函數模板重載看起來不夠,因爲它在關鍵字'template'後面缺少typename參數。也就是說,它應該像template<typename T>結合(1),以便該方法被稱爲函數模板重載,我猜。請給我他們的正確名字。

template <typename T> // T : container 
double Sum(const T &l) // get Container 
{ 
    double sum = 0; 
    T::const_iterator i; 
    for (i = l.begin(); i != l.end(); ++i) { sum += *i; } 
    return sum; 
} 

template <> // get container 
double Sum(const map<string, double> &m) // (1) function template overloading 
// double Sum<map<string, double> >(const map<string, double> &m) // (2) explicit specialization 
{ 
    double sum = 0; 
    map<string, double>::const_iterator i; // obtain Iterator from Container 
    for (i = m.begin(); i != m.end(); i++) { sum += i->second; } // sum. ('first' is index, 'second' is data) 
    return sum; 
} 
+0

否(1)是模板專業化和(2)是Sum的超負荷。 – coincoin

回答

1

兩者都是明確的專業化語法

  • template <> double Sum(const map<string, double> &m);

  • template <> double Sum<map<string, double> >(const map<string, double> &m)

第一個讓編譯器推斷出參數,而第二,你是明確的。

當編譯器不能推導出所有的模板的參數作爲用於第二需要

template <typename T> std::string getNameType(); 

template <> std::string getNameType<int>() { return "int"; } 

或消除歧義的模板函數專門

template <typename T> void foo(T); 

template <typename T> void foo(T*); // overload, not specialization 

//template <> void foo(int*); // Error: do you mean T = int for foo(T*) or T = int* for foo(T) 

template <> void foo<int*>(int*); // specialize foo(T) 
template <> void foo<int>(int*); // specialize foo(T*) 

它通常是更好地使用過載,而不是專門針對功能,因此您的示例:

template <typename Key> 
double Sum(const std::map<Key, double> &m) 
{ 
    double sum = 0; 
    for (const auto& p : m) { sum += p.second; }  return sum; 
} 
+0

非常感謝您的詳細和完整的答案。這真的有幫助。 –

0

對於具體類型,你可能會更好過簡單地定義一個非模板超載:

double Sum(const std::map<std::string, double> &m) // (1) function template overloading 
// double Sum<map<string, double> >(const map<string, double> &m) // (2) explicit specialization 
{ 
    double sum = 0; 
    std::map<std::string, double>::const_iterator i; // obtain Iterator from Container 
    for (i = m.begin(); i != m.end(); i++) { sum += i->second; } // sum. ('first' is index, 'second' is data) 
    return sum; 
} 

模板是比較有用的通用功能:

// 
// sum any map's arithmetic mapped values 
// 
template<class K, class V, class C, class A> 
typename std::map<K, V, C, A>::mapped_type 
Sum(const std::map<K, V, C, A> &m) // (1) function template overloading 
{ 
    using map_type = std::map<K, V, C, A>; 
    using mapped_type = typename map_type::mapped_type; 
    using const_iterator = typename map_type::const_iterator; 
    mapped_type sum = 0; 
    const_iterator i; // obtain Iterator from Container 
    for (i = m.begin(); i != m.end(); i++) { sum += i->second; } // sum. ('first' is index, 'second' is data) 
    return sum; 
} 

.. (C++ 14)...

namespace notstd { 
    template<class...> 
    using void_t = void; 
} 

template< class, class = notstd::void_t<> > 
struct supports_mapped_type : std::false_type { }; 

template< class T > 
struct supports_mapped_type<T, notstd::void_t<typename T::mapped_type>> : std::true_type { }; 

template< class, class = notstd::void_t<> > 
struct supports_const_iterator : std::false_type { }; 

template< class T > 
struct supports_const_iterator<T, notstd::void_t<typename T::const_iterator>> : std::true_type { }; 

template<class T> static constexpr bool IsMaplike = supports_mapped_type<T>() and supports_const_iterator<T>(); 

template<class MapLike, 
std::enable_if_t< 
IsMaplike<MapLike> and std::is_arithmetic<typename MapLike::mapped_type>() 
>* = nullptr> 
typename MapLike::mapped_type 
Sum(const MapLike &m) // (1) function template overloading 
{ 
    using map_type = MapLike; 
    using mapped_type = typename map_type::mapped_type; 
    using const_iterator = typename map_type::const_iterator; 
    mapped_type sum = 0; 
    const_iterator i; // obtain Iterator from Container 
    for (i = m.begin(); i != m.end(); i++) { sum += i->second; } // sum. ('first' is index, 'second' is data) 
    return sum; 
} 

這將現在高興地總結:

std::unordered_map<std::string, int>,並 std::map<std::string, int>

但不 std::map<int, std::string>

+0

我從你的回答中瞭解了很多東西。非常感謝你! –

+0

@DongkyuChoi這是主意。不客氣:) –

0

我沒有完全理解你的問題,因爲你的代碼似乎是好了,但我我會試圖回答。當您手動編寫具有相同名稱但不同參數類型的多個函數時,函數重載是一種方法。例如:

double Sum(const std::vector<double>& l) { 
    //... 
} 

double Sum(const std::list<double>& l) { 
    //... 
} 

double Sum(const std::deque<double>& l) { 
    //... 
} 

在你的榜樣,你寫了一個函數模板:

template <typename T> 
double Sum(const T &l) //... 

和模板專業化:

template <> 
double Sum(const map<string, double> &m) //... 

哪個更好?這取決於你的情況。看到,在函數重載時,你應該自己編寫代碼,而在模板的情況下,編譯器會爲你做!

例如,您一般情況下模板將用於vectorlistqueuedeque和甚至可能是不存在的模板創建的那一刻任何其他兼容的容器中工作。編譯器僅爲那些用於實例化模板的類型生成代碼。如果您嘗試使用不兼容的類型對其進行實例化,則會出現編譯錯誤。只有當你用map<string, double>(它對一般案例模板無效)實例化它時,編譯將會成功,因爲專門化將被選擇用於代碼生成。

正如@RichardHodges所提到的,專業化對您的案例可能是一種矯枉過正;非模板重載應該足夠了。

+0

你的解釋真的很容易理解。非常感謝您的回答! –