65

在 C++ 中,我需要迭代一定次数,但我不需要迭代变量。例如:

for( int x=0; x<10; ++x ) {
    /* code goes here, i do not reference "x" in this code */
}

我意识到我可以通过用 lambda 或命名函数替换“代码到这里”来做到这一点,但这个问题专门针对 for 循环。

我希望 C++11 基于范围的 for 循环会有所帮助:

for( auto x : boost::irange(0,10) ) {
    /* code goes here, i do not reference "x" in this code */
}

但上面给出了一个“未引用的局部变量”,因为我从未明确引用 x。

我想知道是否有更优雅的方式来编写上述 for 循环,以便代码不会生成“未引用的局部变量”警告。

4

10 回答 10

48

现在进行编辑,声明的循环变量减少了 100%。

template <typename F>
void repeat(unsigned n, F f) {
    while (n--) f();
}

将其用作:

repeat(10, f);

或者

repeat(10, [] { f(); });

或者

int g(int);
repeat(10, std::bind(g, 42));

在http://ideone.com/4k83TJ现场观看

于 2013-07-17T23:09:28.157 回答
21

可能有办法做到这一点,但我非常怀疑它会更优雅。您在第一个循环中所拥有的已经是正确的方法,限制了循环变量的范围/生命周期。

我会简单地忽略未使用的变量警告(毕竟这只是编译器表示可能有问题的迹象)或使用编译器工具(如果可用)在那个时候简单地关闭警告。

这可能在某种程度上#pragma取决于您的环境,或者某些实现允许您执行以下操作:

for (int x = 0; x < 10; ++x) {
    (void)x;

    // Other code goes here, that does not reference "x".
}

我已经看到void用于函数体中未使用参数的技巧。

于 2013-07-17T23:02:14.347 回答
9

假设10是一个编译时间常数......

#include <cstddef>
#include <utility>
template<std::size_t N>
struct do_N_times_type {
  template<typename Lambda>
  void operator()( Lambda&& closure ) const {
    closure();
    do_N_times_type<N-1>()(std::forward<Lambda>(closure));
  }
};
template<>
struct do_N_times_type<1> {
  template<typename Lambda>
  void operator()( Lambda&& closure ) const {
    std::forward<Lambda>(closure)();
  }
};
template<>
struct do_N_times_type<0> {
  template<typename Lambda>
  void operator()( Lambda&& closure ) const {
  }
};

template<std::size_t N, typename Lambda>
void do_N_times( Lambda&& closure ) {
  do_N_times_type<N>()( std::forward<Lambda>(closure) );
};
#include <iostream>
void f() {
  std::cout << "did it!\n";
}
int main() {
  do_N_times<10>([&]{
    f();
  });
}

要不就

int main() {
  do_N_times<10>(f);
}

其他荒谬的方法:

编写一个范围迭代器(我称之为 mine index),它产生一系列迭代器-on-integral 类型(我默认为std::size_t)。然后输入:

for( auto _:index_range(10) )

它使用变量 ( _) 但看起来非常混乱。

另一种疯狂的方法是创建一个类似 python 的生成器。编写一个生成器包装器,它接受一个可迭代的范围并生成一个返回std::optionalvalue_type范围的函数并不棘手。

然后我们可以这样做:

auto _ = make_generator( index_range(10) );
while(_()) {
}

它也创建了一个临时变量,而且更加迟钝。

我们可以编写一个在生成器上运行的循环函数:

template<typename Generator, typename Lambda>
void While( Generator&& g, Lambda&& l ) {
  while(true) {
    auto opt = g();
    if (!opt) return;
    l(*opt);
  }
}

然后我们称之为:

While( make_generator( index_range(10) ), [&](auto&&){
  f();
});

但这两者都在函数中创建了一些临时变量,并且比上一个更荒谬,并且依赖于甚至尚未最终确定的 C++1y 的特性。

那些我试图创建一种无变量的方式来重复某件事 10 次的地方。

但实际上,我只是做循环。

您几乎可以肯定地通过键入来阻止警告x=x;

或者写一个函数

template<typename Unused>
void unused( Unused&& ) {}

并调用unused(x);-- 变量x被使用,它的名字被放到里面,所以编译器可能不会在里面警告你。

所以试试这个:

template<typename Unused>
void unused( Unused&& ) {}
for(int x{};x<10;++x) {
  unused(x);
  f();
}

这应该抑制警告,并且实际上很容易理解。

于 2013-07-17T23:10:03.403 回答
9

实际上有一种方法可以使这项工作。您需要做的就是返回一个std::array长度由您提供的常量指定的值:

template <int N>
using range = std::array<int, N>;

int main()
{
    for (auto x : range<5>())
    {
        std::cout << "Awesome\n";
    }
}

输出:

棒棒棒
棒棒棒
棒棒棒
棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒棒

这是一个演示。

注意:这是假设范围说明符是编译时常量,因此如果您必须使用变量,请确保它被有效标记constexpr

于 2013-07-18T00:05:26.677 回答
5

在我看来,您滥用了基于范围的循环。当逻辑是:“为集合中的每个元素做某事”时,应该使用基于范围的循环。整个想法是摆脱索引变量,因为它并不重要。如果您有一个集合,您应该使用必要的 API 对其进行检测,以启用基于范围的迭代。如果您没有集合,则没有使用基于范围的循环的业务(实际上,这就是编译器以不那么有用的方式暗示的内容)。在这种情况下,正常的 for/while 循环是自然的选择。

于 2013-07-18T18:09:02.053 回答
4

Already best answered in https://stackoverflow.com/a/21800058/1147505 : how to define an UNUSED macro to be used throughout your codebase, which suppresses this warning. In a portable way.

于 2014-02-19T11:24:23.323 回答
4

没有任何方法可以为简单地迭代多个数字来创建基于工作的范围。

C++11 基于范围的循环需要一个范围表达式,它可能是:

  • 一个数组或
  • 一类有
    • 成员函数begin()end()
    • 可用的免费功能begin()end()(通过 ADL)

除此之外:基于的范围会产生一些开销:

for ( for_range_declaration : expression ) statement

扩展到

range_init = (expression)
{
  auto && __range = range_init;
  for ( auto __begin = begin_expr,
  __end = end_expr;
  __begin != __end;
  ++__begin ) {
    for_range_declaration = *__begin;
    statement;
  }
}

其中 begin_expr 和 end_expr 是通过数组检查或begin()/end()对获得的。

我认为这不会比简单的 for 循环更“干净”。尤其是在性能方面。没有电话,只是一个简单的循环。

我能想出让它更优雅的唯一方法(优雅显然取决于我的意见)是在这里使用大小或无符号类型:

for(size_t x(0U); x<10U; ++x) f();
于 2013-07-17T23:14:25.357 回答
3

您可以将 STL 与 lambda 表达式一起使用。

#include <algorithm>
#include <iostream>

int main() {
    int a[] = {1,2,3,4,5,6};

    std::for_each(std::begin(a),
            std::end(a),
            [](int){std::cout << "Don't care" << std::endl;});
}

这种方法也适用于任意容器,例如向量或列表。让vector<int> a,然后你会打电话给a.begin()and a.end()。请注意,您还可以使用函数指针而不是 lambda 表达式。

以上保留了您使用 foreach 的概念,而不是抱怨未使用的参数。

于 2013-07-18T18:39:41.460 回答
2

这适用于 GCC 和 clang 以及任何支持 gnu 属性的编译器:

for( [[gnu::unused]] auto x : boost::irange(0,10) ) {

并且应该在任何 c++11 编译器中编译,但如果编译器无法识别 gnu 属性,则可能不会抑制警告。

于 2015-10-16T18:10:20.107 回答
0

参加比赛:

#include <iostream>
#include <boost/range/irange.hpp>
using namespace std;
using namespace boost;

namespace {
    template<typename T> inline void 
    unused(const T&) {}
}

int main() {
    for (auto&& i : irange(0,10)) {
        unused(i);
        cout << "Something" << endl;
    }
    return 0;
}
于 2019-09-23T19:10:14.693 回答