【问题标题】:Specialize function for map like containers专门针对地图类容器的功能
【发布时间】:2014-08-13 06:23:50
【问题描述】:

我想专门为矢量和地图之类的容器创建一个函数模板。对于矢量,我可以像下面那样做,但我不知道我怎样才能有一个专门的函数版本,只用于像容器这样的地图。

#include <iostream>
#include <vector>
#include <map>

using namespace std;

template<typename Iterator>
void print(Iterator begin, Iterator end)
{
    while (begin != end)
    {
        cout << *begin << endl; // compiler error for map like containers
        ++begin;
    }
}

int main()
{
    vector<int> noVec = { 1, 2, 3 };

    print(noVec.begin(), noVec.end());

    map<int, int> nosMap;
    nosMap[0] = 1;
    nosMap[1] = 2;
    nosMap[3] = 3;

    print(nosMap.begin(), nosMap.end());

    return 0;
}

This 问题类似,但它建议在我不想做的向量中使用pair。我知道可以通过 SFINAE 完成专业化,但不知道要检查什么条件。如果我可以使用 C++ 11 type_traits 实现这一点,那就太好了。

【问题讨论】:

标签: c++ c++11


【解决方案1】:

mapvalue_type 是一对,因此您可以检查迭代器的value_type 是否为std::pair,例如

#include <vector>
#include <map>
#include <iostream>

template <typename> 
struct is_pair : std::false_type 
{ };

template <typename T, typename U>
struct is_pair<std::pair<T, U>> : std::true_type 
{ };



template <typename Iter>
typename std::enable_if<is_pair<typename std::iterator_traits<Iter>::value_type>::value>::type
print(Iter begin, Iter end)
{
  std::cout << "called with map-like" << std::endl;
  for (; begin != end; ++begin)
  {
    std::cout << begin->second;
  }
  std::cout << std::endl;
}

template <typename Iter>
typename std::enable_if<!is_pair<typename std::iterator_traits<Iter>::value_type>::value>::type
print(Iter begin, Iter end)
{
  std::cout << "called with vector-like" << std::endl;
  for (; begin != end; ++begin)
  {
    std::cout << *begin;
  }
  std::cout << std::endl;
}



int main()
{
  std::vector<int> vec { 1, 2, 3 };
  std::map<int, int> map {{0, 0}, {1, 1}, {2, 4}, {3, 9}};

  print(vec.begin(), vec.end());
  print(map.begin(), map.end());
}

打印出来的

called with vector-like
123
called with map-like
0149

【讨论】:

  • 我不明白的是如何将 "typename std::iterator_traits::value_type" 转换为 is_pair (true_type) 的两个模板参数。有什么链接可以帮助我理解吗?
  • is_pair 是一个只有一种类型作为模板参数的结构。首先,任何类型的“默认”值都定义为std::false_type,然后is_pair 部分特化为与类型std::pair&lt;T, U&gt; 匹配且值为std::true_type 的任何类型。尽管部分特化有两个模板参数(TU),is_pair只有一个单个模板参数,即std::pair&lt;T, U&gt;。跨度>
【解决方案2】:

你不需要专门化任何东西。您所要做的就是为std::pair 提供重载输出operator&lt;&lt;,如下例所示:

template<typename T1, typename T2>
std::ostream& operator<<(std::ostream &out, std::pair<T1, T2> const &mp) {
  return (out << "(" << mp.first << ", " << mp.second << ")");
}

LIVE DEMO


编辑:

然而,如 cmets 中建议的 @Benjamin Lindley ,上述解决方案可能与输出 operator&lt;&lt; 的其他模板重载为 std::pair 冲突。

如果是这种情况,您也可以在自己的命名空间(例如,namespace detail)中编写两个模板函数重载(例如,print_elem),如下例所示:

namespace detail {
  template<typename T1, typename T2>
  std::ostream& print_elem(std::ostream &out, std::pair<T1, T2> const &mp) {
    return (out << "(" << mp.first << ", " << mp.second << ")");
  }

  template<typename T>
  std::ostream& print_elem(std::ostream &out, T const &elem) {
      return (out << elem);
  }
}

并更改您的template print,如下例所示:

template<typename Iterator>
void print(Iterator begin, Iterator end)
{
    while (begin != end) {
        detail::print_elem(cout, *begin) << endl;
        ++begin;
    }
}

LIVE DEMO

【讨论】:

  • 可能很多人都有将operator&lt;&lt; 重载为std::pair 的想法,而你的函数会与他们的函数发生冲突。我建议不要直接使用cout &lt;&lt;,而是在您自己的命名空间中创建一对单独的重载函数。无论如何+1,因为尽可能狭隘地专门化比专门化整个功能更好,这两种情况的大部分都是相同的。
  • 重载operator&lt;&lt; 适用于将值打印到控制台的特定解决方案。如果模板函数应该做其他事情怎么办?从服务中获取数据,存储在数据库中,等等?此解决方案行不通。
  • 我喜欢我们可以重载 operator
  • @MariusBancila:所以,编写重载函数来做你想做的事情,然后调用它们。
  • @SivaChandran 您的选择很受尊重。但是,我更喜欢“occam 的剃刀”哲学(即使用最简单的)。恕我直言,制作一对重载比使用高级 C++ 模板功能(如 SFINAE)要简单得多。
猜你喜欢
  • 2019-06-27
  • 1970-01-01
  • 1970-01-01
  • 2011-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-21
  • 1970-01-01
相关资源
最近更新 更多