2016-09-15 77 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

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