代码之家  ›  专栏  ›  技术社区  ›  Raindog

Boostlambda或Phoenix问题:使用std::for_each对容器的每个元素进行操作

  •  2
  • Raindog  · 技术社区  · 16 年前

    我在清理一些旧代码时遇到了一个问题。这是功能:

    uint32_t ADT::get_connectivity_data( std::vector< std::vector<uint8_t> > &output )
    {
        output.resize(chunks.size());
        for(chunk_vec_t::iterator it = chunks.begin(); it < chunks.end(); ++it)
        {
            uint32_t success = (*it)->get_connectivity_data(output[it-chunks.begin()]);
        }
        return TRUE;
    }
    

    我感兴趣的是将for循环清理为lambda表达式,但很快就陷入了如何准确地传递正确的参数来获取连接数据的困境。get_connectivity_data通过引用获取std::vector并用一些数据填充它。输出包含每个“块”的std::vector。

    基本上,我的结论是,它更容易,更干净,而且 更短的 让我的代码保持原样。

    编辑:

    所以我的问题最接近我想象的答案是:

     std::for_each( chunks.begin(), chunks.end(), 
                       bind( &chunk_vec_t::value::type::get_connectivity_data, 
                             _1, 
                             output[ std::distance( _1, chunks.begn() ] 
                           )
                     );
    

    但是代码没有编译,我对代码做了一些修改以使其编译,但我遇到了两个问题:

    1. _ 1是一个智能ptr,std::Distance不起作用,我认为我需要使用&chunks[0]作为开始。
    2. 由于1是一个智能指针,我必须执行以下操作:&chunk_vec_t::value_type::value type::get_u connectivity_u data,这导致VC9编译器崩溃…

    关于Zip_uuIterators的答案看起来不错,直到我深入了解它并发现对于这个特定的用途,需要大量的额外代码(绑定这个和那个等等)。

    编辑2:

    我发现了一个可以接受的解决方案,它既缺乏无关语法,又清晰明了,我在这里和下面都发布过。

    std::transform(chunks.begin(), chunks.end(), back_inserter(tmp), boost::bind(&ADTChunk::get_connectivity_data, _1) );
    
    6 回复  |  直到 15 年前
        1
  •  3
  •   Raindog    16 年前

    经过一番努力,我想出了这个解决方案:

    std::transform(chunks.begin(), chunks.end(), back_inserter(tmp), boost::bind(&ADTChunk::get_connectivity_data, _1) );
    

    它要求我更改get_connectivity_data以返回std::vector而不是引用一个,还要求我将块的元素更改为boost::shared_ptr而不是loki::smartptr。

        2
  •  2
  •   endian    16 年前

    我认为您认为最好的做法是保持代码不变是正确的。如果你很难理解(a)你在写它,(b)你理解你正试图解决的确切问题,想象一下,如果有人在3年后出现,并且必须理解你写的问题和解决方案,这将是多么困难。

        3
  •  2
  •   oz10    16 年前

    如果没有看到整个类的代码,就很难确定什么是有效的。就我个人而言,我认为在这种情况下,Boost ForEach更干净,但为了参考起见,我可能会尝试使用lambdas执行类似的操作(注意,我无法测试编译)

    uint32_t ADT::get_connectivity_data( std::vector< std::vector<uint8_t> > &output )
    {
        using namespace boost::lambda;
    
        output.resize( chunks.size() );
    
        std::for_each( chunks.begin(), chunks.end(), 
                       bind( &chunk_vec_t::value::type::get_connectivity_data, 
                             _1, 
                             output[ std::distance( _1, chunks.begn() ] 
                           )
                     );
        return TRUE;
    }
    
        4
  •  1
  •   1800 INFORMATION    16 年前

    关于lambda,我真的不知道你在说什么,但是我可以对涉及STL容器的代码清理提出一些一般性的建议。

    使用所有STL容器类型的typedef:

    typedef std::vector<uint8_t> Chunks;
    typedef std::vector<Chunks> Output;
    uint32_t ADT::get_connectivity_data( Output &output )
    

    既然你在谈论使用Boost,就用 Boost.Foreach :

    BOOST_FOREACH(chunk_vec_t::value_type &chunk, chunks)
      uint32_t success =
        chunk->get_connectivity_data(output[std::distance(&chunk, chunks.begin())]);
    

    在黑暗中刺伤“lambda”的东西:

    typedef const boost::function2<uint32_t, chunk_vec_t::value_type, Chunks>
      GetConnectivity;
    uint32_t ADT::get_connectivity_data(Output &output, GetConnectivity &getConnectivity)
    {
      output.resize(chunks.size());
      BOOST_FOREACH(chunk_vec_t::value_type &chunk, chunks)
        uint32_t success =
          getConnectivity(chunk, output[std::distance(&chunk, chunks.begin())]);
      return TRUE;
    }
    

    然后你可以这样称呼它:

    get_connectivity_data(output,
      boost::bind(&chunk_vec_t::value_type::get_connectivity_data, _1, _2));
    
        5
  •  1
  •   James Hopkin    16 年前

    实际上,您正在对两个容器并行执行操作。这就是Boost::Zip_迭代器的设计目的。

    但是,您需要并行处理容器的唯一原因是chunk::get_connectivity_data提取了一个out参数。如果要按值返回(使用异常来报告错误),可以只使用插入迭代器。

        6
  •  1
  •   fizzer    16 年前

    出于某种原因,STL初学者总是坚持使用vector::iterator,而不是更易读(和常量时间)的操作符[]。表达式 it-chunks.begin() 应该告诉原作者,他已经输了SmartAss编码游戏,毕竟需要一个简陋的索引:

    for (size_t i = 0, size = chunks.size(); i < size; ++i)
    {
        chunks[i]->get_connectivity_data(output[i]);
    } 
    

    OP还可以考虑丢失伪返回代码,并将其作为常量成员函数。

    推荐文章