Boost C++ 库

……是世界上评价最高、设计最精良的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu《C++ Coding Standards》

陷阱 - Boost C++ 函数库
PrevUpHomeNext

本节介绍 BOOST_FOREACH 的一些常见陷阱。

带逗号的类型

由于 BOOST_FOREACH 是一个宏,它必须正好有两个参数,并且它们之间必须正好有一个逗号分隔。这并不总是方便,尤其是在循环变量的类型是模板时。考虑尝试迭代一个 std::map

std::map<int,int> m;

// ERROR! Too many arguments to BOOST_FOREACH macro.
BOOST_FOREACH(std::pair<const int,int> p, m) // ...

修复此问题的一种方法是使用 typedef。

std::map<int,int> m;
typedef std::pair<const int,int> pair_t;

BOOST_FOREACH(pair_t p, m) // ...

另一种修复方法是预先声明循环变量。

std::map<int,int> m;
std::pair<const int,int> p;

BOOST_FOREACH(p, m) // ...

提升和迭代器失效

在底层,BOOST_FOREACH 使用迭代器来遍历元素序列。在循环执行之前,结束迭代器会被缓存到局部变量中。这被称为 提升,这是一项重要的优化。然而,它假定序列的结束迭代器是稳定的。通常情况是这样,但如果我们修改序列(在迭代过程中添加或删除元素),我们可能会“搬起石头砸自己的脚”。

考虑以下代码:

std::vector<int> vect(4, 4);
BOOST_FOREACH(int i, vect)
{
    vect.push_back(i + 1);
}

此代码将编译,但行为未定义。这是因为在逻辑上它等同于以下代码:

std::vector<int> vect(4, 4);
for(std::vector<int>::iterator it1 = vect.begin(), it2 = vect.end();
    it1 != it2; ++it1)
{
    int i = *it1;
    vect.push_back(i + 1); // Oops! This invalidates it1 and it2!
}

vect.push_back() 的调用将导致 vect 的所有迭代器失效,包括 it1it2。循环的下一次迭代将使用失效的迭代器。这很糟糕。

这个故事的寓意是,在添加和删除正在迭代的序列中的元素之前,要三思而后行。如果这样做可能导致迭代器失效,请不要这样做。改为使用常规的 for 循环。


PrevUpHomeNext