在没有警告(或编译指示)的情况下进行便携式"assign within conditional expressions"的好方法?

Nice way to portably "assign within conditional expressions" without warnings (or pragmas)?

本文关键字:assign 便携式 within conditional 方法 expressions 情况下 警告 编译 指示      更新时间:2023-10-16

我有一段这样的代码:

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++等编译器的警告。

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

我不想关闭它们(它们很有用),也不想插入#pragma(它们在编译器之间不可移植)
我只想写一些简短的东西,告诉典型的编译器,";是的,我打算在这里分配";。

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

使用好的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());
}

实时代码

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

因此,一种方法是明确这一点。

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

因为我疯了,如果必须解决这个问题,我会写一个函数iterators,当给定sequencefor( : )循环处理的对象)时,它会产生迭代器的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次迭代器。

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

在这种情况下,将其编写为do/while循环可能最简单,因此它只在执行循环体之后测试条件

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

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

在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)
}

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

for (Iter i = items.begin();
          i != items.end() || (finished = !finished, finished);
          finished ? i : ++i, ++offset)
{
    // ...
}