版权所有 © 2004 Eric Niebler
根据 Boost 软件许可证版本 1.0 发布。(参见随附文件 LICENSE_1_0.txt 或在 https://boost.ac.cn/LICENSE_1_0.txt 复制)
“让简单的事情变得容易。”
-- Larry Wall
BOOST_FOREACH
?在 C++ 中,编写一个遍历序列的循环很麻烦。我们可以使用迭代器,这需要大量的样板代码,或者我们可以使用 std::for_each()
算法并将循环体移入谓词,这同样需要不少样板代码,并且迫使我们将逻辑移到远离其使用的地方。相比之下,像 Perl 这样的其他语言提供了一个专用的“foreach”构造来自动化这个过程。BOOST_FOREACH
就是 C++ 的这样一个构造。它为我们遍历序列,使我们不必直接处理迭代器或编写谓词。
BOOST_FOREACH
设计用于易用性和效率。它不做动态分配,不进行虚函数调用或通过函数指针调用,也不进行对编译器优化器不透明的调用。这带来了近乎最优的代码生成;BOOST_FOREACH
的性能通常与同等的手动编码循环相差只有几个百分点。而且,尽管 BOOST_FOREACH
是一个宏,但它是一个行为异常良好的宏。它只评估其参数一次,不会产生令人讨厌的意外。
下面是一个使用 BOOST_FOREACH
循环遍历 std::string
内容的示例程序。
#include <string> #include <iostream> #include <boost/foreach.hpp> int main() { std::string hello( "Hello, world!" ); BOOST_FOREACH( char ch, hello ) { std::cout << ch; } return 0; }
该程序输出如下:
Hello, world!
BOOST_FOREACH
遍历序列。但到底什么才算序列呢?由于 BOOST_FOREACH
构建在 Boost.Range 之上,因此它自动支持 Boost.Range 识别为序列的类型。具体来说,BOOST_FOREACH
可以与满足 Single Pass Range Concept 的类型一起使用。例如,我们可以使用 BOOST_FOREACH
来处理
char
和 wchar_t
)![]() |
注意 |
---|---|
对 STL 容器的支持非常通用;任何看起来像 STL 容器的都算。如果它具有嵌套的 |
请参阅 Extensibility 部分,了解如何使 BOOST_FOREACH
与其他类型一起工作。
下面是一些演示我们可以使用 BOOST_FOREACH
的各种方式的示例。
遍历 STL 容器
std::list<int> list_int( /*...*/ ); BOOST_FOREACH( int i, list_int ) { // do something with i }
遍历数组,具有协变性(即,迭代变量的类型与容器的元素类型不完全相同)
short array_short[] = {1,2,3}; BOOST_FOREACH( int i, array_short ) { // The short was implicitly converted to an int }
预先声明循环变量,并在循环体中使用 break
、continue
和 return
std::deque<int> deque_int( /*...*/ ); int i = 0; BOOST_FOREACH( i, deque_int ) { if( i == 0 ) return; if( i == 1 ) continue; if( i == 2 ) break; }
通过引用遍历序列,并修改底层序列
short array_short[] = { 1, 2, 3 }; BOOST_FOREACH( short & i, array_short ) { ++i; } // array_short contains {2,3,4} here
使用嵌套的 BOOST_FOREACH
循环遍历向量的向量。在此示例中,请注意循环体周围的大括号不是必需的
std::vector<std::vector<int> > matrix_int; BOOST_FOREACH( std::vector<int> & row, matrix_int ) BOOST_FOREACH( int & i, row ) ++i;
遍历按值返回序列的表达式(即右值)
extern std::vector<float> get_vector_float(); BOOST_FOREACH( float f, get_vector_float() ) { // Note: get_vector_float() will be called exactly once }
反向迭代
std::list<int> list_int( /*...*/ ); BOOST_REVERSE_FOREACH( int i, list_int ) { // do something with i }
在某些旧编译器上,遍历右值不起作用。请查看 Portability 部分,了解您的编译器是否支持此功能。
BOOST_FOREACH
更美观人们对 BOOST_FOREACH
这个名字抱怨过。它太长了。ALL CAPS
看起来会让人厌烦。这可能是真的,但 BOOST_FOREACH
仅仅遵循 Boost Naming Convention。但这并不意味着您就必须接受它。如果您想使用不同的标识符(例如 foreach_
),您可以简单地这样做:
#define foreach_ BOOST_FOREACH #define foreach_r_ BOOST_REVERSE_FOREACH
只有当您确定选择的标识符不会在您的代码中引起名称冲突时,才执行此操作。
![]() |
注意 |
---|---|
请勿使用 |
最后,有一点警告。很多人使用 foreach
宏作为 BOOST_FOREACH
的简写形式。我不提倡这样做。这会导致 BOOST_FOREACH
宏内部的名称冲突,因为 foreach
是一个命名空间的名字。此外,foreach
是一个足够常见的标识符;就连 Qt 也将其定义为宏。如果您坚持使用 foreach
,您可以尝试这样做:
#include <boost/foreach.hpp> namespace boost { // Suggested work-around for https://svn.boost.org/trac/boost/ticket/6131 namespace BOOST_FOREACH = foreach; } #define foreach BOOST_FOREACH
这将解决您可能遇到的一些问题,但并非所有问题。请优先使用其他标识符。