gcc和cpp中的" foreach值"宏

时间:2020-03-05 18:57:35  来源:igfitidea点击:

我有一个C ++中经常使用的" foreach"宏,该宏适用于大多数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宏的其他实例冲突。如果我可以在迭代器变量名称后加上当前行号,则可以使用,但是我不知道该怎么做。

解决方案

回答

我们可以定义一个模板类,该类将mymap的类型作为模板参数,并通过重载*和->来像对值进行迭代的行为。

回答

我们会在寻找BOOST_FOREACH,他们已经为我们完成了所有工作!

如果确实要自己滚动,则可以在C ++中的任何地方声明一个块,这可以解决中间问题itr-> second的作用域问题。
...

// 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
}

回答

我们是否考虑过使用Boost库?他们实现了一个foreach宏,它可能比我们将要编写的任何东西都更强大...而且还有transform_iterator似乎可以用来完成所需内容的第二次提取。

不幸的是,由于我对C ++不够了解,所以我无法确切告诉我们如何使用它:)谷歌搜索提供了一些有希望的答案:comp.lang.c ++。moderated,Boost transform_iterator用例。

回答

我们可以使用两个循环来执行此操作。第一个声明了迭代器,其名称是容器变量的函数(如果我们担心与自己的代码发生冲突,可以使此丑陋)。第二个声明值变量。

#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)

通过使用相同的循环终止条件,外部循环只会发生一次(如果幸运的话,它会被优化掉)。另外,如果映射为空,则避免在迭代器上调用-> second。对于内循环的增量,这也是三元运算符的原因。最后,我们将var保留为最后一个值,因为不会再次引用它。

我们可以内联ci(con​​tainer),但是我认为它使宏更具可读性。

回答

STL转换功能也执行类似的操作。

参数为(按顺序):

  • 指定容器开始的输入迭代器
  • 指定容器末尾的输入迭代器
  • 一个输出迭代器,用于定义将输出放置在何处(对于就地转换,类似于for-each,只需在#1中传递输入迭代器)
  • 在每个元素上执行的一元函数(函数对象)

对于一个非常简单的示例,我们可以通过以下方式将字符串中的每个字符大写:

#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"
}

或者,还有一个累加函数,它允许在调用函数对象之间保留一些值。累加不会像变换一样修改输入容器中的数据。

回答

Boost :: For_each是迄今为止最好的选择。有趣的是,它们实际上给是宏BOOST_FOREACH(),我们可以将其包装并#define为我们真正想要在代码中调用的内容。大多数人都会选择老式的" foreach",但是其他商店可能具有不同的编码标准,因此符合这种思维方式。 Boost还为C ++开发人员提供了许多其他好处!很值得使用。

回答

#define foreach(var, container) for (typeof((container).begin()) var = (container).begin(); var != (container).end(); ++var)

C ++中没有typeof ...如何为我们编译? (当然不是便携式的)

回答

我创建了一个带有一些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提供了以下宏:

  • foreach()-指针的常规foreach
  • foreach_()-局部变量的常规foreach
  • foreachdel()-foreach版本,用于检查循环内是否删除,指针版本
  • foreachdel_()-foreach版本,其中检查循环内是否删除,局部变量版本

他们一定会为我工作,我希望他们也会使生活更轻松:)