提升lambda或phoenix问题:使用std :: for_each对容器的每个元素进行操作

时间:2008-10-22 06:49:16

标签: c++ boost-lambda boost-phoenix

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

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的问题。 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不起作用,我想我需要使用&amp; chunks [0]作为开始
  2. 由于_ 1是智能指针,我不得不这样做:&amp; chunk_vec_t :: value_ type :: ValueType :: get_ connectivity_ data导致VC9编译器崩溃......
  3. 关于zip_迭代器的答案看起来很好,直到我阅读更多内容并发现对于这种特殊用途,所需的额外代码量很大(绑定这个和那个等)。

    EDIT2:

    我找到了一个可接受的解决方案,这个解决方案在无关语法和清晰方面都很低,我已经在这里和下面发布了。

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

6 个答案:

答案 0 :(得分:3)

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

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。

答案 1 :(得分:2)

我认为你认为最好的办法是保持代码原样是正确的。如果你很难理解它(a)你正在写它并且(b)你理解你想要解决的确切问题,想象当有人在3年时间出现时会有多困难。必须了解你所写的问题和解决方案。

答案 2 :(得分:2)

如果没有看到整个班级的代码,很难确定什么会起作用。就个人而言,我认为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;
}

答案 3 :(得分:1)

关于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));

答案 4 :(得分:1)

您实际执行的操作是并行执行两个容器上的操作。这就是boost :: zip_iterator的设计目的。

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

答案 5 :(得分:1)

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

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

OP也可能会考虑丢失虚假返回码并使其成为const成员函数。