【问题标题】:Is there an easy way to iterator over a static list of strings in C++有没有一种简单的方法来迭代 C++ 中的静态字符串列表
【发布时间】:2009-05-22 18:19:19
【问题描述】:

我经常需要在我的 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"}) {
   ...
}

肯定有人以前做过这个。

【问题讨论】:

    标签: c++ string iterator


    【解决方案1】:

    这是我的尝试。遗憾的是,它依赖于 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。但它也会将数组转换为指针,我发现这有时不是我们想要的。然后将结果类型用作数组元素类型。

    要在枚举变量具有依赖类型的模板中使用,您必须使用另一个将typename 放在boost::remove_referenceboost::function_traits 之前的宏。可以将其命名为 SEQ_FOR_EACH_D(D == 依赖)。

    【讨论】:

      【解决方案2】:

      请注意,如果标记数组的末尾,则处理 C 字符串数组会更容易:

      const char* strs[] = { "abc", "xyz", "123", NULL };
      for (int i=0; strs[i] != NULL  i++) {
        ...
      }
      

      【讨论】:

        【解决方案3】:

        类似这样的:

        
        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++ 循环相比没有优势!
        • 哦,来吧,看看计算静态数组长度的其他答案,函数可以替换为仿函数实例,优点是不必包含 Boost 标头,即仅使用标准库,没有优势在 C++ 循环上,因为,for_each 是一个 C++ 循环。
        【解决方案4】:

        Cou 可以使用 va_arg hack 来创建一个返回可迭代集合的函数(请注意,这真的是 hack!)

        新的 C++ 标准 (C++0x) 将提供更方便的初始化方式 (initializer lists)

        另一种可能性是将boost::assignmentFOREACH 结合使用。

        注意BOOST::FOREACH 也适用于数组!

        【讨论】:

          【解决方案5】:

          创建一个返回数组大小的宏在这里会有所帮助。

          #define N_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
          

          那么你的原始代码看起来还不错。

          for(int i = 0; i < N_ELEMS(strs); ++i) {
            ...
          }
          

          这是遍历任何静态数组的好习惯,而不仅仅是字符串数组。

          【讨论】:

            【解决方案6】:

            而不是这个:

            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)”,因为这样可以防止您更改数据类型时出现问题。
            • 确实如此(虽然我会说“sizeof(strs)/sizeof(strs[0])”);我在没有仔细阅读的情况下复制并粘贴了他的实现。我也会命名它,例如"const size_t sizeof_strs = ...etc..." 在数组定义之后的那一行,这样我就可以在后续语句(例如 for 循环)中使用 const 标识符而不是 long 表达式。
            【解决方案7】:

            这个应该可以(我没有测试,所以可能有一些错别字)

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

            然后在循环中使用item:

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

            【讨论】:

            • 预处理器不喜欢这个列表。一个简单的测试:#define M1(L) L M1({1,2,3,4}) 我从 g++ 宏“M1”中得到这个,传递了 4 个参数,但只需要 1 个。我对编写宏了解不够定义来解决这个问题。
            【解决方案8】:

            我会尝试直接在阵列上使用 BOOST_FOREACH。 documentation 似乎认为它会起作用。

            事实上确实如此。它必须是两行,但它有效:

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2012-01-21
              • 1970-01-01
              • 2019-12-07
              • 2017-05-13
              • 2010-10-25
              • 1970-01-01
              • 1970-01-01
              • 2016-01-19
              相关资源
              最近更新 更多