C ++ 11基于范围的循环,没有循环variables
在C + +我需要迭代一定的次数,但我不需要一个迭代variables。 例如:
for( int x=0; x<10; ++x ) { /* code goes here, i do not reference "x" in this code */ }
我意识到我可以通过用“lambda”或“named”函数replace“code goes here”来做到这一点,但是这个问题是关于for循环的。
我希望C ++ 11的基于范围的循环将有助于:
for( auto x : boost::irange(0,10) ) { /* code goes here, i do not reference "x" in this code */ }
但上面给出了“未引用的局部variables”,因为我从来没有明确地引用x。
我想知道是否有更优雅的方式来编写上面的循环,以便代码不会生成“未引用的局部variables”警告。
可能有办法做到这一点,但我非常怀疑它会更优雅。 你在第一个循环中已经是正确的方法,限制循环variables的范围/生命周期。
我会简单地忽略未使用的variables警告(这只是编译器指出可能是错误的一个指示),或者使用编译器工具(如果可用)来简单地closures此时的警告。
现在编辑声明为100%的循环variables。
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));
假设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); }
其他荒谬的方法:
编写一个范围迭代器(我称之为我的index
),它产生一系列迭代器 – 整型types(我默认为std::size_t
)。 然后键入:
for( auto _:index_range(10) )
它使用一个variables( _
),但看起来非常混乱。
另一个疯狂的方法是创build一个类似python的生成器。 编写一个生成可迭代范围的生成器包装器,并生成一个函数,该函数返回该范围的value_type
的std::optional
。
我们可以这样做:
auto _ = make_generator( index_range(10) ); while(_()) { }
这也创造了一个临时variables,甚至更加钝。
我们可以编写一个在发生器上运行的循环函数:
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(); });
但是这两个函数都在函数中创build了一些临时variables,而且比最后一个更可笑,而且依赖于C ++ 1y的特性,这个特性还没有最终确定。
那些我试图创造一个variablesless的方式重复10次的东西。
但是,真的,我只是做循环。
你几乎可以肯定的通过键入x=x;
来阻止这个警告x=x;
或者写一个函数
template<typename Unused> void unused( Unused&& ) {}
并呼叫unused(x);
– 使用了variablesx
,并将其名称放入内部,因此编译器可能不会在内部警告您。
所以试试这个:
template<typename Unused> void unused( Unused&& ) {} for(int x{};x<10;++x) { unused(x); f(); }
这应该压制警告,而且实际上很容易理解。
实际上有一种方法可以使这项工作。 所有你需要做的是返回一个std::array
,其长度由你提供的常量指定:
template <int N> using range = std::array<int, N>; int main() { for (auto x : range<5>()) { std::cout << "Awesome\n"; } }
输出:
真棒
真棒
真棒
真棒
真棒
这里是一个演示。
注意:这是假定范围说明符是一个编译时常量,所以如果你必须使用一个variables,确保它被有效标记为constexpr
。
没有任何方法可以使工作范围的基础上迭代几个数字。
C ++ 11基于范围的循环需要一个范围expression式,可能是:
- 一个数组或
- 有一个类也有
- 成员函数
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循环更清洁。 特别是在性能方面。 没有电话,只是一个简单的循环。
唯一的方法,我可以弄清楚,使其更优雅(其中雅致明显受我的意见)是通过在这里使用大小或无符号types:
for(size_t x(0U); x<10U; ++x) f();
已经在https://stackoverflow.com/a/21800058/1147505中得到了最好的回答:如何定义一个在整个代码库中被使用的UNUSEDmacros,这会抑制这个警告。; 以便携的方式。
你可以使用STL和一个lambdaexpression式。
#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()
和a.end()
。 请注意,您也可以使用函数指针而不是lambdaexpression式。
上面保留了使用foreach的概念,而不是抱怨一个未使用的参数。
在我看来,你滥用基于范围的循环。 当逻辑是“ 为集合中的每个元素做某事 ”时,应该使用基于范围的循环。 整个想法是摆脱索引variables,因为它并不重要。 如果你有一个集合,你应该使用必要的API来启用基于范围的迭代。 如果你没有一个集合,你没有业务使用基于范围的循环(实际上,这是编译器以不那么信息的方式所暗示的)。 在这种情况下,正常的/ while循环是自然的select。
这在GCC和clang以及任何支持gnu属性的编译器中都有效:
for( [[gnu::unused]] auto x : boost::irange(0,10) ) {
并且应该在任何c ++ 11编译器中进行编译,但如果编译器不能识别gnu属性,则可能不会抑制警告。