类似于地图的类型的C ++模板专门化

时间:2012-06-25 06:31:53

标签: c++ templates

我想定义一个用于打印类似std::map类型内容的通用函数。我最初的尝试是这样的函数:

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 ++的新手,所以我不知道这类东西的确切术语。这被称为“模板专业化”吗?

3 个答案:

答案 0 :(得分: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 :(得分:2)

答案很简单。

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

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

注意,使用template s时,您不需要inline个关键字。

答案 2 :(得分: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还需要在将来的某个版本中打印该信息,那么您可能需要使用模板模板参数,该参数将这两种类型作为参数。