[英]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.