Boost C++ 库

...世界上最受推崇和设计最精良的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu,《C++ 编码标准

PrevUpHomeNext

第 12 章。Boost.Foreach

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 适用于满足 单程范围概念 的类型。例如,我们可以将 BOOST_FOREACH 与以下类型一起使用

  • STL 容器
  • 数组
  • 空终止字符串(charwchar_t
  • std::pair 迭代器
[Note] 注意

对 STL 容器的支持非常通用;任何看起来像 STL 容器的东西都算数。如果它具有嵌套的 iteratorconst_iterator 类型以及 begin()end() 成员函数,则 BOOST_FOREACH 将自动知道如何遍历它。正是通过这种方式,boost::iterator_range<>boost::sub_range<>BOOST_FOREACH 一起工作。

请参阅关于 可扩展性 的章节,了解如何使 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
}

预先声明循环变量,并在循环体中使用 breakcontinuereturn

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
}

在某些旧编译器上,遍历右值不起作用。请查看可移植性部分,以查看您的编译器是否支持此功能。

使 BOOST_FOREACH 更美观

人们抱怨 BOOST_FOREACH 这个名称。它太长了。ALL CAPS 可能看起来很累人。这可能是真的,但是 BOOST_FOREACH 只是遵循了 Boost 命名约定。但这并不意味着您就只能使用它。如果您想使用不同的标识符(例如 foreach_),您可以简单地执行

#define foreach_         BOOST_FOREACH
#define foreach_r_       BOOST_REVERSE_FOREACH

仅当您确定您选择的标识符不会在您的代码中引起名称冲突时才执行此操作。

[Note] 注意

不要使用 #define foreach_(x,y) BOOST_FOREACH(x,y)。如果参数本身是宏,这可能会有问题。这将导致这些宏的额外扩展。相反,请使用上面显示的形式。

最后,一个警告。很多人使用 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

这将解决您可能遇到一些问题,但不是全部。最好使用不同的标识符。


PrevUpHomeNext