C++ 11基于范围的循环,没有循环变量

non*_*gon 55 c++ c++11

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

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

我意识到我可以通过用lambda或命名函数替换"code goes here"来做到这一点,但这个问题特别针对for循环.

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

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

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

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

seh*_*ehe 45

现在编辑,声明的循环变量减少100%.

template <typename F>
void repeat(unsigned n, F f) {
    while (n--) f();
}
Run Code Online (Sandbox Code Playgroud)

用它作为:

repeat(10, f);
Run Code Online (Sandbox Code Playgroud)

要么

repeat(10, [] { f(); });
Run Code Online (Sandbox Code Playgroud)

要么

int g(int);
repeat(10, std::bind(g, 42));
Run Code Online (Sandbox Code Playgroud)

访问http://ideone.com/4k83TJ直播


pax*_*blo 18

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

我只是忽略未使用的变量警告(它只是编译器指示某些东西可能是错误的),或者使用编译器工具(如果可用)来简单地关闭那时的警告.

这可能是某种形式,#pragma具体取决于您的环境,或者某些实现允许您执行以下操作:

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

    // Other code goes here, that does not reference "x".
}
Run Code Online (Sandbox Code Playgroud)

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

  • 我认为,实际建议人们忽略编译器警告是危险的.这导致程序编译了大量(被忽略的)警告,这些警告隐藏了偶尔想要的,重要的和有用的警告.我永远不会接受我的项目的补丁,如果没有一个警告就不能编译,正是因为这个原因...... (9认同)
  • 很难忽视*某些*警告.这意味着你必须记住它,只要你维护代码,如果一个新的开发人员来了,他必须明白警告是无害的......我肯定会使用某种`UNUSED(var)`宏来压制警告. (4认同)
  • @cmaster,我不建议他们盲目地忽略所有警告,只是在代码的_that_点警告_that_.一旦你理解了警告并且意识到你比编译器更清楚,这样做是很好的.既然我很可能使用了"any"这个词让你认为我的意思是'全部'而不是按照预期的零或一,我删除了它:-) (2认同)

Yak*_*ont 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();
  });
}
Run Code Online (Sandbox Code Playgroud)

要不就

int main() {
  do_N_times<10>(f);
}
Run Code Online (Sandbox Code Playgroud)

其他荒谬的方法:

编写一个范围迭代器(我称之为我的index),它产生一系列迭代器上的积分类型(我默认为std::size_t).然后输入:

for( auto _:index_range(10) )
Run Code Online (Sandbox Code Playgroud)

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

另一个疯狂的方法是创建一个类似python的生成器.写一个发生器的包装,需要一个可迭代范围,并产生一个返回功能std::optionalvalue_type范围内的是不棘手.

然后我们可以这样做:

auto _ = make_generator( index_range(10) );
while(_()) {
}
Run Code Online (Sandbox Code Playgroud)

这也创造了一个临时变量,甚至更加钝.

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

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

然后我们称之为:

While( make_generator( index_range(10) ), [&](auto&&){
  f();
});
Run Code Online (Sandbox Code Playgroud)

但是这两者都在函数中创建了一些临时变量,并且比上一个更加荒谬,并且依赖于C++ 1y的功能,这些功能甚至还没有最终确定.

那些我尝试创建一个无变量的方法重复10次的东西.

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

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

或者写一个函数

template<typename Unused>
void unused( Unused&& ) {}
Run Code Online (Sandbox Code Playgroud)

并调用unused(x);- 使用该变量x,并将其名称删除,因此编译器可能不会在内部向您发出警告.

试试这个:

template<typename Unused>
void unused( Unused&& ) {}
for(int x{};x<10;++x) {
  unused(x);
  f();
}
Run Code Online (Sandbox Code Playgroud)

这应该压制警告,实际上很容易理解.

  • 为什么所有模板都神奇?我的答案是3行,并且还将编译为完全展开的循环,用于编译时常量`n` ...(最近在gcc/clang上验证) (6认同)
  • 这些看起来很可怕!这是一个荒谬的代码量,所有这些都是为了避免首先出现的编译器警告.我的意思是,这在代码高尔夫方面令人印象深刻,但我无法考虑在生产代码中使用这些类似的东西.大多数人花在试图弄清楚究竟发生了什么的时间上,并不值得为没有迭代变量而获得的任何抽象增益. (3认同)
  • 为什么1特殊套装?似乎没必要 (2认同)
  • 只要查看代码,我就能说出它是由你编写的. (2认同)

0x4*_*2D2 8

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

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

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

输出:

真棒
真棒
真棒
真棒
真棒

这是一个演示.

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


Som*_*ame 5

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


evn*_*vnu 5

您可以将 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;});
}
Run Code Online (Sandbox Code Playgroud)

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

上面保留了使用 foreach 的概念,同时不抱怨未使用的参数。