我有一个'foreach'宏我经常在C ++中使用,适用于大多数STL容器:
#define foreach(var, container) \
for(typeof((container).begin()) var = (container).begin(); \
var != (container).end(); \
++var)
(注意'typeof'是gcc扩展名。)它的使用方式如下:
std::vector< Blorgus > blorgi = ...;
foreach(blorgus, blorgi) {
blorgus->draw();
}
我想做一些类似于迭代地图值的东西。也许称之为“foreach_value”。所以不要写
foreach(pair, mymap) {
pair->second->foo();
}
我会写
foreach_value(v, mymap) {
v.foo();
}
我无法想出一个会执行此操作的宏,因为它需要声明两个变量:迭代器和值变量(上面的'v')。我不知道如何在for循环的初始化器中做到这一点,即使使用gcc扩展。我可以在foreach_value调用之前声明它,但是它会与同一范围内的foreach_value宏的其他实例冲突。如果我可以将当前行号后缀为迭代器变量名,那么它可以工作,但我不知道该怎么做。
答案 0 :(得分:8)
你会寻找BOOST_FOREACH - 他们已经为你完成了所有的工作!
如果你想自己推出,你可以在C ++的任何地方声明一个块,这可以解决你的中间存储空间的问题 - >&gt;秒 ...
// Valid C++ code (which does nothing useful)
{
int a = 21; // Which could be storage of your value type
}
// a out of scope here
{
int a = 32; // Does not conflict with a above
}
答案 1 :(得分:4)
您可以使用两个循环执行此操作。第一个声明了迭代器,其名称是容器变量的函数(如果您担心与自己的代码冲突,可以使这个更丑陋)。第二个声明了值变量。
#define ci(container) container ## iter
#define foreach_value(var, container) \
for (typeof((container).begin()) ci(container) = container.begin(); \
ci(container) != container.end(); ) \
for (typeof(ci(container)->second)* var = &ci(container)->second; \
ci(container) != container.end(); \
(++ci(container) != container.end()) ? \
(var = &ci(container)->second) : var)
通过使用相同的循环终止条件,外循环只发生一次(如果幸运的话,会被优化掉)。此外,如果地图为空,则避免在迭代器上调用 - &gt;秒。这与三元运算符在内循环增量中的原因相同;最后,我们只将var留在最后一个值,因为它不会再被引用。
你可以内联ci(容器),但我认为它使宏更具可读性。
答案 2 :(得分:3)
STL transform 功能也做类似的事情。
参数是(按顺序):
对于一个非常简单的示例,您可以通过以下方式将字符串中的每个字符大写:
#include <iostream>
#include <string>
#include <algorithm>
#include <cctype>
int main(int argc, char* argv[]) {
std::string s("my lowercase string");
std::transform(s.begin(), s.end(), s.begin(), toupper);
std::cout << s << std::endl; // "MY LOWERCASE STRING"
}
另外还有 accumulate 函数,它允许在调用函数对象之间保留一些值。与 transform 的情况一样, accumulate 不会修改输入容器中的数据。
答案 3 :(得分:1)
您是否考虑过使用Boost libraries?他们有一个foreach
macro implemented,它可能比你写的任何东西都更强大......还有transform_iterator
似乎可以用来做你所做的第二次提取部分想。
不幸的是我无法告诉你如何使用它因为我不太了解C ++ :) This Google search出现了一些有希望的答案:comp.lang.c++.moderated,{ {3}}
答案 4 :(得分:1)
Boost :: For_each是迄今为止最好的选择。漂亮的是,他们实际给你的是宏BOOST_FOREACH(),然后你可以将它包装和#define到你真正想在代码中调用它的任何东西。大多数人都会选择好的旧“foreach”,但其他商店可能有不同的编码标准,所以这符合这种心态。 Boost还为C ++开发人员提供了许多其他好东西!非常值得使用。
答案 5 :(得分:1)
我使用foreach()的一些变体创建了一个小的Foreach.h帮助器,包括操作局部变量和指针的两个变体,还有一个额外的版本可以防止从循环中删除元素。所以使用我的宏的代码看起来很漂亮和舒适:
#include <cstdio>
#include <vector>
#include "foreach.h"
int main()
{
// make int vector and fill it
vector<int> k;
for (int i=0; i<10; ++i) k.push_back(i);
// show what the upper loop filled
foreach_ (it, k) printf("%i ",(*it));
printf("\n");
// show all of the data, but get rid of 4
// http://en.wikipedia.org/wiki/Tetraphobia :)
foreachdel_ (it, k)
{
if (*it == 4) it=k.erase(it);
printf("%i ",(*it));
}
printf("\n");
return 0;
}
输出:
0 1 2 3 4 5 6 7 8 9
0 1 2 3 5 6 7 8 9
我的Foreach.h提供以下宏:
他们确实为我工作,我希望他们也会让你的生活更轻松一点:)
答案 6 :(得分:1)
这个问题分为两部分。你需要以某种方式(1)在map的值(而不是键)上生成迭代器(或者更确切地说,是一个可迭代的序列),并且(2)使用宏来进行迭代而不需要很多样板
最干净的解决方案是对部分(1)使用Boost Range Adaptor,对部分(2)使用Boost Foreach。您不需要编写宏或自己实现迭代器。
#include <map>
#include <string>
#include <boost/range/adaptor/map.hpp>
#include <boost/foreach.hpp>
int main()
{
// Sample data
std::map<int, std::string> myMap ;
myMap[0] = "Zero" ;
myMap[10] = "Ten" ;
myMap[20] = "Twenty" ;
// Loop over map values
BOOST_FOREACH( std::string text, myMap | boost::adaptors::map_values )
{
std::cout << text << " " ;
}
}
// Output:
// Zero Ten Twenty
答案 7 :(得分:0)
您可以定义一个模板类,它将mymap的类型作为模板参数,并通过重载*和 - &gt;来充当值的迭代器。
答案 8 :(得分:0)
#define foreach(var, container) for (typeof((container).begin()) var = (container).begin(); var != (container).end(); ++var)
C ++中没有typeof ...这对你来说是如何编译的? (它肯定不便携)
答案 9 :(得分:0)
我根据foreach_value
Boost
代码实施了自己的foreach
:
#include <boost/preprocessor/cat.hpp>
#define MUNZEKONZA_FOREACH_IN_MAP_ID(x) BOOST_PP_CAT(x, __LINE__)
namespace munzekonza {
namespace foreach_in_map_private {
inline bool set_false(bool& b) {
b = false;
return false;
}
}
}
#define MUNZEKONZA_FOREACH_VALUE(value, map) \
for(auto MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) = map.begin(); \
MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) != map.end();) \
for(bool MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) = true; \
MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) && \
MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) != map.end(); \
(MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue)) ? \
((void)++MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it)) : \
(void)0) \
if( munzekonza::foreach_in_map_private::set_false( \
MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue))) {} else \
for( value = MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it)->second; \
!MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue); \
MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) = true)
例如,您可以在代码中使用它:
#define MUNZEKONZA_FOREACH_VALUE foreach_value
std::map<int, std::string> mymap;
// populate the map ...
foreach_value( const std::string& value, mymap ) {
// do something with value
}
// change value
foreach_value( std::string& value, mymap ) {
value = "hey";
}
答案 10 :(得分:0)
#define zforeach(var, container) for(auto var = (container).begin(); var != (container).end(); ++var)
没有typeof()所以你可以使用它:
decltype((container).begin()) var
decltype(container)::iterator var