在C++中,有没有一种简单的方法来迭代静态字符串列表?

5

我经常需要在我的C++代码中遍历字符串列表。

在像Perl这样的语言中,这很容易:

foreach my $x ("abc", "xyz", "123") {.... }

过去,我在C++中做过以下工作

const char* strs[] = { "abc", "xyz", "123" };
for (int i=0; i<sizeof(strs)/sizeof(const char*); i++) {
   const char *str = strs[i];
   ...

如果我已经有一个STL容器,我可以使用BOOST_FOREACH。

std::vector<std::string> strs;
BOOST_FOREACH(std::string str, strs) {
   ...

我曾试图创建一个宏来组合所有这些概念,但没有成功。

我希望能够编写类似于以下代码的内容:

SPECIAL_STRING_FOREACH(const char* str, {"abc", "xyz", "123"}) {
   ...
}

当然,之前肯定有人已经想到过这个了。
8个回答

9

这是我尝试的方法。不幸的是,它依赖于可变参数宏,这是C99/C++1x功能。但在GCC中可以正常工作。

#include <boost/foreach.hpp>
#include <boost/type_traits.hpp>
#include <iostream>

#define SEQ_FOR_EACH(D, ...)                                        \
    if(bool c = false) ; else                                       \
        for(boost::remove_reference<boost::function_traits<void(D)> \
                ::arg1_type>::type _t[] = __VA_ARGS__;              \
            !c; c = true)                                           \
            BOOST_FOREACH(D, _t)

int main() {
    SEQ_FOR_EACH(std::string &v, { "hello", "doctor" }) {
        std::cout << v << std::endl;
    }
}

请注意,您也可以使用引用变量进行迭代,以避免无用的复制。以下是使用boost.preprocessor(a)(b)...语法的示例,在预处理阶段编译成相同的代码。
#define SEQ_FOR_EACH(D, SEQ)                                          \
    if(bool c = false) ; else                                         \
        for(boost::remove_reference<boost::function_traits<void(D)>   \
                ::arg1_type>::type _t[] = { BOOST_PP_SEQ_ENUM(SEQ) }; \
            !c; c = true)                                             \
            BOOST_FOREACH(D, _t)

int main() {
    SEQ_FOR_EACH(std::string &v, ("hello")("doctor")) {
        std::cout << v << std::endl;
    }
}

关键在于组装一个函数类型,该函数类型的参数为枚举变量,并获取该参数的类型。然后使用 boost::remove_reference 来移除任何引用。第一个版本使用了 boost::decay。但是它也会将数组转换为指针,这有时不是想要的结果。所得到的类型随后用作数组元素类型。
在模板中使用时,如果枚举变量具有依赖类型,则必须使用另一个宏,在 boost::remove_referenceboost::function_traits 之前添加 typename。可以将其命名为 SEQ_FOR_EACH_D(D == dependent)。

8
请注意,如果标记数组的结尾,则处理C字符串数组会更容易:
const char* strs[] = { "abc", "xyz", "123", NULL };
for (int i=0; strs[i] != NULL  i++) {
  ...
}

4

类似这样:


void func( const char* s ) { /* ... */ }

const char* array[] = { "abc", "xyz", "123" };
std::for_each( array, array + 3, func );

您可能还想看看boost::array


I) 请提供计算3!的代码。 II) 因为函数指针间接性而效率低下! III) 与boost::foreach或常规C++循环相比没有优势! - Dario
1
噢,来看看其他计算静态数组长度的答案吧。函数可以被一个函数对象实例替换,优势在于不必包含Boost头文件,也就是只使用标准库,而没有比C ++循环更具优势,因为for_each本身就是一个C ++循环。 - Nikolai Fetissov

3
制作一个返回数组大小的宏能够在这里发挥作用。
#define N_ELEMS(a) (sizeof(a) / sizeof((a)[0]))

那么你的原始代码看起来并不那么糟糕。
for(int i = 0; i < N_ELEMS(strs); ++i) {
  ...
}

这是一个很好的习语,可以迭代任何静态数组,不仅仅是字符串数组。


2
你可以使用 va_arg 技巧创建一个返回可迭代集合的函数(请注意,这确实是一个技巧!)
新的 C++ 标准(C++0x)将提供一种更方便的初始化方式(初始化列表
另一种可能性是使用 boost::assignmentFOREACH 结合使用。
请注意,BOOST::FOREACH 也适用于数组!

0

不要这样写:

for (int i=0; i<sizeof(strs)/sizeof(const char*); i++)
{
    const char *str = strs[i];
    ...

考虑一下:

for (const char *ptr = strs[0],
    *end = strs[sizeof(strs)/sizeof(const char*)];
    ptr < end; ++ptr)
{
    ...

你可能会发现这个表单更容易宏化;无论如何,ptr变量模拟了一个迭代器。

肯定有人之前就已经想到过这个了。

我怀疑他们应该没有。一个for循环是惯用的且易于阅读(特别是如果你知道数组的大小),用户定义的宏是非标准的。


我更喜欢使用 "sizeof(strs)/sizeof(*strs)",因为这样可以避免在更改数据类型时出现问题。 - Richard Corden
这是真的(虽然我会说“sizeof(strs)/ sizeof(strs [0])”); 我在没有仔细阅读它的情况下复制并粘贴了他的实现。我还会将其命名为“const size_t sizeof_strs = ... etc ...”,放在数组定义后面的那一行,以便我可以在后续语句(如for循环)中使用const标识符而不是长表达式。 - ChrisW

0

应该可以了(我没有测试过,所以可能会有一些错别字)

#define STR_ARRAY_FOREACH(I) const char* Items[] = I; for( const char *item = Items[0], *end = strs[ sizeof( Items ) / sizeof( const char* ) ]; item < end; ++item )

然后在循环中使用项目:

STR_ARRAY_FOREACH({ "abc", "xyz", "123" })
{
    cout << item << "\n";
}

预处理器不喜欢列表。 一个简单的测试: #define M1(L) L M1({1,2,3,4})我从g++得到这个报错信息。 宏“M1”传递了 4 个参数,但只需要 1 个。我对编写宏定义并不能了解得足够多以修复此问题。 - mmccoo

0

我建议直接在数组上使用BOOST_FOREACH。文档似乎认为这样做是可行的。

事实上,它确实可行。虽然需要两行代码,但它可以正常工作:

const char * myarray[] = {"abc", "xyz", "123"};
BOOST_FOREACH (const char *str, myarray) {
  std::cout << "Hello " << str << std::endl;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接