[英]Function template overloading vs Explicit specialization
我設置了兩個模板函數,它們為不同的STL容器獲取總和:一個用於列表和向量,另一個用於映射。
請參閱第二個模板函數定義的注釋行(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;
}
兩者都是顯式的特化語法
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;
}
對於具體類型,您最好只是定義一個非模板重載:
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>
我沒有完全理解你的問題,因為你的代碼似乎沒問題,但我會嘗試回答。 當您手動編寫具有相同名稱但不同參數類型的多個函數時,函數重載是一種方法。 例如:
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) //...
哪個更好? 這取決於你的情況。 看看,有了函數重載,你應該自己編寫代碼,而在模板的情況下編譯器會為你做!
例如,您的通用案例模板將適用於vector
, list
, queue
, deque
和任何其他兼容容器,這些容器在創建模板時甚至可能不存在。 編譯器僅為那些用於實例化模板的類型生成代碼。 如果您嘗試使用不兼容的類型對其進行實例化,則會出現編譯錯誤。 並且只有使用map<string, double>
(對於一般案例模板無效)實例化它,編譯才會成功,因為將選擇特化來生成代碼。
正如@RichardHodges所提到的那樣,專業化可能對你的案件來說太過分了; 非模板重載應該足夠了。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.