【问题标题】:C++11 auto iterator type with std::begin(), std::end() issue带有 std::begin()、std::end() 问题的 C++11 自动迭代器类型
【发布时间】:2013-07-16 07:36:51
【问题描述】:

我有一些私有类成员,表示包含一些数据的std::deque 的随机访问数组:

std::vector<std::deque<SomeDataClass> > someMember;

我想提供一个公共类方法,它返回可迭代的数据结构,包含我的双端队列数组中的所有数据元素:

std::deque<SomeDataClass> someMethod();

我希望这个方法遍历向量中的所有双端队列,并将沿途的每个元素复制到本地 std::deque,最终按值返回这个本地 std::deque。 我正在尝试使用 C++11 autostd::begin()std::end() 来实现此方法:

std::deque<SomeDataClass> MyClassName::someMethod(){
    std::deque<DirectedEdge> allDataItems;
    std::deque<DirectedEdge>::iterator deqIter = allDataItems.begin();
    for(auto it = std::begin(someMember); it != std::end(someMember); ++it){
        std::copy(std::begin(*it), std::end(*it), deqIter);
    }

    return allDataItems;
}

我在 deque 标头中的运行时收到数据访问冲突未处理的异常错误。什么是错误?

【问题讨论】:

  • 在使用迭代器时,您应该始终使用!= 作为结束条件。没有什么能说明下一个在记忆中更靠前。
  • 是的,谢谢。我会编辑我的问题,愚蠢的错误。我仍然有访问冲突。
  • @chris:只要容器是向量,除了标准之外,没有什么比这更重要的了。无论如何,对于随机访问迭代器,operator&lt; 不会告诉您内存中的顺序,而是逻辑顺序中的顺序(对于其他迭代器类别,根本没有定义)。由于 vard 代码中使用的唯一容器是 vector 和 deque,它们都有随机访问迭代器,因此在它们上使用 operator&lt; 非常好(当然,只要两个迭代器属于同一个容器)。
  • @celtschk,我知道有人会对此发表评论。是的,随机访问迭代器可以与operator&lt; 一起正常工作,但我重视能够在不破坏其他所有内容的情况下进行更改。使用 operator!= 适用于每个迭代器类型。我应该把它说得更清楚一点,但是当我想到它的时候,我的五分钟已经过去了。

标签: c++ c++11 std


【解决方案1】:

std::copy() 要求目标范围足够大以容纳副本,但allDataItems 为空。您必须提前在allDataItems 中预留空间(但std::deque 无法做到这一点)。您应该改用std::back_inserter(在&lt;iterator&gt; 中定义):

std::deque<SomeDataClass> MyClassName::someMethod(){
    std::deque<DirectedEdge> allDataItems;
    for(auto it = std::begin(someMember); it < std::end(someMember); ++it){
        std::copy(std::begin(*it), std::end(*it), std::back_inserter(allDataItems));
    }

    return allDataItems;
}

【讨论】:

  • 考虑改用allDataItems.insert(std::end(allDataItems), ...);。我会说尽可能使用成员通常会更好。
  • @chris 您可以将其作为单独的答案,以便 OP 决定他们更喜欢哪个。
  • 只需将其添加到您的,然后会有更好的答案:)
  • != 代替 &lt; 工作,这意味着如果 someMember 不是随机访问容器,代码仍然可以工作。如果不需要迭代器,for( auto const&amp; x : someMember ) 会更清晰更短。
【解决方案2】:

这是一种惯用的 C+11 方法:

std::deque<SomeDataClass> MyClassName::someMethod() {
  std::deque<DirectedEdge> allDataItems;

  for( auto const& dq : someMember ) {
    allDataItems.insert( allDataItems.end(), std::begin(dq), std::end(dq) );
  }
  return allDataItems;
}

另一种方法是编写concatinate 函数:

struct concatenate {
  template<typename Dest, typename Src>
  Dest&& operator()( Dest&& d, Src const& s ) const {
    using std::begin; using std::end;
    typename std::decay<Dest>::type retval = std::forward<Dest>(d);
    retval.insert( end(retval), begin(s), end(s) );
    return std::move(retval);
  }
};
std::deque<SomeDataClass> MyClassName::someMethod() {
  using std::begin; using std::end; // enable ADL
  return std::accumulate(
    begin(someMember), end(someMember),
    std::deque<DirectedEdge>(), concatenate()
  );
}

这很可爱。如果你不喜欢std::accumulate

std::deque<SomeDataClass> MyClassName::someMethod() {
  std::deque<DirectedEdge> allDataItems;

  for( auto const& dq : someMember ) {
    allDataItems = concatenate( std::move(allDataItems), dq );
  }
  return allDataItems;
}

两者的效率大致相当。

【讨论】:

  • @benvoigt 根据要求将函数重命名为 merriam_webster
猜你喜欢
  • 2015-04-29
  • 2015-12-05
  • 1970-01-01
  • 2014-01-24
  • 2014-10-03
  • 2012-12-04
  • 2020-07-12
  • 2013-10-25
  • 1970-01-01
相关资源
最近更新 更多