2012-06-25 144 views
1

我想定义一个通用函数来打印std::map类似的内容。我最初的尝试是这样的功能:类似地图类型的C++模板专门化

template <class K, class V> 
inline void PrintCollection(const std::map<K,V>& map, 
          const char* separator="\n", 
          const char* arrow="->", 
          const char* optcstr="") { 
    typedef typename std::map<K,V>::const_iterator iter_type; 
    std::cout << optcstr; 
    for (iter_type begin = map.begin(), it = begin, end = map.end(); 
     it != end; ++it) { 
    if (it != begin) { 
     std::cout << separator; 
    } 
    std::cout << it->first << arrow << it->second; 
    } 
    std::cout << std::endl; 
} 

它工作正常。当我试图将此功能推广一步时,即使它适用于std::multimap类型时,编译器变得生气。我尝试了几种方法,使std::map通用的函数定义,如:

template <class M, class K, class V> 
inline void PrintCollection(const M<K,V>& map, 
          const char* separator="\n", 
          const char* arrow="->", 
          const char* optcstr="") { 
    typedef typename M<K,V>::const_iterator iter_type; 
    std::cout << optcstr; 
    for (iter_type begin = map.begin(), it = begin, end = map.end(); 
     it != end; ++it) { 
    if (it != begin) { 
     std::cout << separator; 
    } 
    std::cout << it->first << arrow << it->second; 
    } 
    std::cout << std::endl; 
} 

没有成功。

我如何概括这个函数,就像我上面定义的那样?

为了更清楚起见,我已经定义了一个为此函数之前定义的类向量类定义的函数。它就像

template <class T> 
inline void PrintCollection(const T& collection, 
          const char* separator="\n", 
          const char* optcstr="") { 
    typedef typename T::const_iterator iter_type; 

    std::cout << optcstr; 

    for (iter_type begin = collection.begin(), it = begin, end = collection.end(); 
     it != end; 
     ++it) { 
    if (it != begin) { 
     std::cout << separator; 
    } 
    std::cout << *it; 
    } 

    std::cout << std::endl; 
} 

所以我想要实现它,使这个功能专门针对地图类。我在C++中很新,所以我不知道这种东西的确切名词。这被称为“模板专业化”吗?

回答

4

像stdlib那样做,并在算法接口中使用迭代器。这是最通用的解决方案。

template<class Iter> 
void PrintCollection(Iter first, Iter last, 
        const char* separator="\n", 
        const char* arrow="->", 
        const char* optcstr="") 
{ 
    typedef Iter iter_type; 
    std::cout << optcstr; 
    for (iter_type begin = first, it = begin, end = last; 
     it != end; ++it) { 
    if (it != begin) { 
     std::cout << separator; 
    } 
    std::cout << it->first << arrow << it->second; 
    } 
    std::cout << std::endl; 
} 


int main() 
{ 
    vector<pair<int, int>> collection; 
    map<int, int> collection2; 
    pair<int, int> collection3[3]; 

    PrintCollection(begin(collection), end(collection)); 
    PrintCollection(begin(collection2), end(collection2)); 
    PrintCollection(begin(collection3), end(collection3)); 
} 
1

你可以使用一个模板,模板参数

template<template<class, class> class M, class K, class V> 
inline void PrintCollection(const M<K, V>& map, /* rest as before */) 
{ 
    // rest as before 
} 

int main() 
{ 
    std::map<int, int> m1; 
    std::multi_map<int, int> m2; 

    // fill both maps 

    PrintCollection(m1); 
    PrintCollection(m2); 
} 

但作为hansmaad时指出,你也可以使用一对迭代器,而不是容器作为参数。一般来说,如果您的PrintCollection是非常通用的,并且不使用它具有KeyValue类型的事实,则您更喜欢该解决方案。 OTOH,如果您的PrintCollection也需要在将来的某个版本中打印该信息,那么您可能需要使用将这两种类型作为参数的模板参数。

+0

地图实际上有四个模板参数,其中一些是默认的。 –

+0

@BoPersson好点。它可能会解决模板别名可能,但也许不值得的麻烦。 – TemplateRex

2

答案相当简单。

函数中的类型名称KV没有依赖关系。所以删除它们并制作一个通用模板。它可以同时用于mapmultimap

template <class AnyMap> 
void PrintCollection(const AnyMap& map, 
    ... 
{ 
    typedef typename AnyMap::const_iterator iter_type; 

在侧面说明,与template S,你不需要inline关键字。

+0

我想写这个PrintCollection函数适用于任何容器类型,比如std :: vector等。所以我粘贴的代码实际上是一个模板专业化的类地图类。在此之前,我还有另一个适用于连续容器的功能。 – Haldun

+0

@Haldun,所有的容器里面都没有'first'和'second'数据成员。如果您删除特定的代码,我的解决方案将适用于任何容器。这是[map/multimap](http://ideone.com/A7VFP)的演示。 – iammilind