【问题标题】:Iterating over pair elements in a container of pairs (C++)迭代对容器中的对元素(C++)
【发布时间】:2010-12-21 18:24:44
【问题描述】:

如果我有一个容器(vectorlist 等),其中每个元素都是 std::pair,是否有一种简单的方法可以遍历每对中的每个元素?

std::vector<std::pair<int,int> > a;
a.push_back(std::pair(1,3));
a.push_back(std::pair(2,3));
a.push_back(std::pair(4,2));
a.push_back(std::pair(5,2));
a.push_back(std::pair(1,5));

然后能够迭代值:1,3,2,3,4,2,5,2,1,5?

同样,什么类型的函子/函数会返回给我一个容器(相同类型),其中包含上述对元素的平面列表?

【问题讨论】:

  • std:pair 并不要求两种模板类型都属于同一类型,因此使用方法来做你想做的事情并没有什么意义。
  • 考虑重新考虑您选择的容器。看来你这里不需要一双。

标签: c++ iterator containers std-pair


【解决方案1】:

首先,您必须创建自己的迭代器类,它将指示对内位置的标志与 container&lt;pair&gt; 迭代器配对

对于第二个,它更容易,尽管要尽可能通用(相同类型的容器),您需要一个template typedef。这里只是矢量:

template <class V>
std::vector<V> flatten_pairs(std::vector<std::pair<V,V> > const& a) {
  typedef std::vector<std::pair<V,V> > A;
  std::vector<V> ret;
  for (typename A::const_iterator i=a.begin(),e=a.end();i!=e;++i) {
    ret.push_back(i->first);
    ret.push_back(i->second);
  }
  return ret;
}

这是你如何伪造一个模板 typedef:

template <class C>
struct same_container;

template <class V>
struct same_container<std::vector<V> > {
  template <class W> struct rebind { typedef std::vector<W> type; };
};

template <class V>
struct same_list<std::list<V> > {
  template <class W> struct rebind { typedef std::list<W> type; };
};

template <class C>
typename same_container<C>::rebind<typename C::value_type::first_type>::type
flatten_pairs(C const& a);

【讨论】:

  • 应该是std::pair&lt;V,V&gt;
  • 感谢您注意到这一点 - 已修复。
【解决方案2】:

以下代码将根据需要打印所有值:

for ( size_t x = 0; x < a.size(); ++x ) {
    cout << a[x].first << "," << a[x].second << ",";
}

比起创建自定义迭代器,我更喜欢这种简单的方法。

【讨论】:

  • 总的来说我同意。如果您的容器已经在概念上(并且实际上)持有对,那么使用它就像您在概念上持有对一样。如果有理由将其视为一维数组,请使用遵循该概念范式的容器和访问方法。如果您需要在一个地方以一种方式思考它,而在其他地方以另一种方式思考它,那么您才应该担心适配器或特殊迭代器或转换或其他任何东西,并且只有当您对这些不同的方式有充分的理由时考虑您的数据。
【解决方案3】:

要将您的对容器扁平化为第二个容器,您还可以简单地编写自己的插入器:

template<class C>
struct Inserter {
    std::back_insert_iterator<C> in;
    Inserter(C& c) : in(c) {}
    void operator()(const std::pair<typename C::value_type, typename C::value_type>& p)
    {
        *in++ = p.first;
    *in++ = p.second;
    }
};

template<class C>
Inserter<C> make_inserter(C& c)
{ 
    return Inserter<C>(c); 
}

// usage example:
std::list<int> l;
std::for_each(a.begin(), a.end(), make_inserter(l));

【讨论】:

  • 让我们都花点时间希望 C++ 有协程,这将使 1->many and many->1 迭代器适配器变得轻而易举,并且意味着您可以迭代所需的元素,在相同数量的代码行中,无需复制列表。啊,协程。
  • 或者只是意识到对象在概念上更容易,并且几乎总是可以替代协程。他们从一开始就在 C++ 中。老实说,协程只是捆绑了状态+函数,类也是如此。
  • 当然,但我说的是代码行。协程不会神奇地做图灵机做不到的任何事情,但它确实让您yield 无需显式保存状态,记住您在做什么,然后在重新进入时执行切换或其他操作功能。至于“概念上更容易” - 嗯。协程可以通过生产者-消费者管道做一些很酷的事情,这正是这些迭代器适配器的本质(或者更确切地说,如果它们不基于传统的函数调用,它们将会是什么)。
  • 什么是轻而易举?原来我第一次听到这个词。
【解决方案4】:

没有简单的方法来执行你想要的迭代,但你可能想看看 boost::iterator_adaptor 库或实现你自己的迭代器来完成它(它不应该太复杂)。然后,在第二个问题上,您可以将 std::copy 与新的迭代器适配器一起使用。

【讨论】:

    【解决方案5】:

    不,std::pair 真的没有这样的东西。您可能要考虑改用 Boost Tuple。元组有点像std::pair 的扩展版本,它允许任意数量的元素(有一定限制,但通常至少为 10),并且还可以访问向量/数组之类的元素(即你可以通过名称或索引访问元素)。

    TR1 还包括 std::tr1::tuple,它是 Boost 元组的子集,但如果有记忆,它仍然包括您要求的名称/索引功能。

    编辑:请注意,在这两种情况下,索引符号都需要索引的 compile-time 常量,因此您不能编写(运行时)循环来迭代一个元组——但你可以通过一些元编程来完成这项工作。 Boost fusion 包含相当多的功能来支持您需要的东西(巧合的是,tuple 是 fusion 库的一部分)。

    【讨论】:

      【解决方案6】:

      在某些时候,您需要使用 firstsecond,即使您创建了自己的迭代器类。我认为没有办法摆脱它(至少,以便携的方式)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-09-23
        • 1970-01-01
        • 1970-01-01
        • 2018-08-19
        • 2015-01-19
        • 2015-06-06
        • 1970-01-01
        相关资源
        最近更新 更多