本节介绍 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
的所有迭代器失效,包括 it1
和 it2
。循环的下一次迭代将使用失效的迭代器。这很糟糕。
这个故事的寓意是,在添加和删除正在迭代的序列中的元素之前,要三思而后行。如果这样做可能导致迭代器失效,请不要这样做。改为使用常规的 for
循环。