-1

我有一段这样的代码:

bool finished = false;
size_t offset = 0;
for (Iter i = items.begin();
          i != items.end() || (finished = !finished);
          finished ? i : ++i, ++offset)
{
    do_some_work();
    if (some_condition(other_collection[offset]))
    {
        i = items.insert(i, new_value());
        do_more_work();
        if (blah) { continue; }
    }
    do_more_work();
}

目标是执行所有迭代器的主体,包括end().

我收到来自 Visual C++ 等编译器的“条件表达式中的赋值”警告。

避免这些警告的最简单方法是什么?

我不想关闭它们(它们很有帮助)并且我不想插入#pragmas(它们在编译器之间不可移植)。
我只想写一些简短的东西来告诉典型的编译器,“是的,我打算在这里分配”。

我发现的最简单的解决方案是创建一个函数(例如assign)并调用它,但我想知道是否有任何方法也可以避免为此定义一个新函数。

4

5 回答 5

5

使用好的 ol' for i

#include <vector>
#include <iostream>


int main() {
    std::vector<int> v = { 1, 5, 23, 2, 44 };
    auto iter = v.begin();
    for (std::size_t i = 0; i != v.size() + 1; ++i, ++iter) {
        if(iter == v.end()) {
            std::cout << "END!" << std::endl;
        }
        else {
            std::cout << *iter << std::endl;
        }
    }
}

实时代码


更新:作为对您的评论的回应,我已经修改了要使用的代码std::distance,使您能够只使用迭代器而不直接知道迭代器范围的大小。

#include <vector>
#include <iostream>


template<typename IterType>
void print(IterType iter /* begin */, IterType end) {
    auto size = std::distance(iter, end);
    for (decltype(size) i = 0; i != size + 1; ++i, ++iter) {
        if(iter == end) {
            std::cout << "END!" << std::endl;
        }
        else {
            std::cout << *iter << std::endl;
        }
    }
}


int main() {
    std::vector<int> v = { 1, 5, 23, 2, 44 };
    print(v.begin(), v.end());
}

实时代码

于 2013-08-19T04:32:05.063 回答
5

在这种情况下,您不会迭代容器的内容。相反,您正在迭代容器的有效迭代器。

因此,一种方法是明确表示。

创建一个所述迭代器的序列,其end元素是end该序列的最后一个迭代器的过去一个。

因为我疯了,如果我必须解决这个问题,我会写一个函数iterators,当给定一个sequence(一个for( : )循环工作的对象)时,它会产生一个sequence迭代器而不是底层类型的序列。

它需要一个enum参数来说明它是否包含end迭代器。它将默认为独占。

你会这样使用它:

for( auto it : iterators( sequence, include_end ) ) {
  // code
}

编写该函数的工作并非微不足道,但它会使使用点的循环看起来非常干净。

以理智的方式编写此代码将涉及使用boost迭代器库。稍微不那么理智将涉及重新实现boost迭代器库的一部分,无论是逐字还是通过手工编写更糟,然后使用它。写它而不复制它的精神或文字boost将是一个坏主意。

template<typename iterator>
struct iterator_iterator: boost::iterator_facade<
  iterator_iterator<iterator>, iterator,
  typename std::iterator_traits<iterator>::iterator_category,
  iterator const&,
  typename std::iterator_traits<iterator>::difference_type
>:{
  // sufficient state:
  iterator current;
  iterator src_end;
  bool past_end_iterator;
  // now, implement the core operations.  Note that
  // random advance has to be careful, because we cannot advance
  // current beyond src_end.  Note we should implement every one
  // of the methods in the requirements (including advance), but
  // only the ones that the underlying iterator's category requires
  // should be called and hence instantiated.
  iterator dereference() const { return current; }
  bool equal( iterator_iterator<iterator> other ) const {
    if (past_end_iterator || other.past_end_iterator)
      return past_end_iterator && other.past_end_iterator;
    return current == other.current;
  }
  void increment() {
    if (current == src_end) {
      past_end_iterator = true;
    } else {
      ++current;
    }
  }
  void decrement() {
    if (past_end_iterator) {
      past_end_iterator = false;
    } else {
      --current;
    }
  }
  void advance( std::ptrdiff_t n ) {
    if (n==0)
      return;
    if (n==1) {
      increment();
    } else if (n==-1) {
      decrement();
    }
    if ((n>0) && ( current+(n-1) == src_end ) {
      current = src_end;
      past_end_iterator = true;
    } else if ((n<0) && past_end_iterator) {
      past_end_iterator = false;
      ++n;
      current = src_end + n;
    } else {
      current += n;
    }
  }
  typename std::iterator_traits<iterator>::difference_type distance_to(
    iterator_iterator<iterator> other
  ) const {
    if (past_end_iterator || other.past_end_iterator) {
      if (past_end_iterator && other.past_end_iterator) {
        return 0;
      using std::distance;
      auto retval = distance( current, other.current );
      if (past_end_iterator)
        return retval-1;
      else
        return retval+1;
    }
  }
};

或类似的东西。(未编译,只是编写)您将使用一对开始/结束迭代器,并创建一对开始/结束迭代器或开始/过去结束迭代器迭代器,具体取决于您是否要包含结束迭代器在迭代器的迭代中。

我怀疑上面的 CRTP 是堆栈溢出代码中迭代器一词的最高有用使用密度之一:在 5 行代码中使用了 13 次迭代器。

于 2013-08-19T04:39:15.710 回答
3

如果您要执行 for 循环end,即使它等于begin,您的循环也总是至少执行一次。

既然如此,写成do/while循环可能是最简单的,所以它只在执行循环体后测试条件:

bool continuing;
i = begin;
do { 
    whatever();
} while ((continuing = (i != end)) && (++i, continuing));

丑陋的终止条件针对 end 测试迭代器的当前值,然后在(且仅当)需要执行另一个迭代时为下一次迭代递增迭代器(然后使用原始比较中的值来继续/中断循环) .

于 2013-08-19T04:34:37.267 回答
1

在 Visual Studio 2010 (C++) 中有一些关于这个问题的讨论:暂时抑制 C4706 警告

您可能会尝试使用!!来暗示您打算将(finished = !finished)视为布尔值(它是!按顺序排列的两个一元,并将 0 转换为 0 并将其他所有内容转换为 1):

bool finished = false;
for (Iterator i = begin; i != end || !! (finished = !finished) ; finished ? i : ++i)
{
    // (body)
}
于 2013-08-19T04:29:43.933 回答
0

没关系,我自己找到了解决方案......

for (Iter i = items.begin();
          i != items.end() || (finished = !finished, finished);
          finished ? i : ++i, ++offset)
{
    // ...
}
于 2013-08-19T05:02:33.880 回答