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



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

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_connection_data的困境。get_connectivity_data通过引用获取一个std::向量,并用一些数据填充它。输出包含每个"chunk"的std::矢量。

基本上,我的结论是,保持代码原样会更容易、更干净、更短

编辑:

因此,正如我所设想的,我的问题最接近的答案是:

 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::ValueType::get_connectivity_data导致VC9编译器崩溃

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

第2版:

我找到了一个可接受的解决方案,它既不涉及无关语法,又清晰明了,我已经在这里和下面发布了它。

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

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

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。

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

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

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

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

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

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

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

由于某些原因,STL初学者总是坚持使用向量::迭代器,而不是更可读的(和恒定时间)运算符[]。表达式it-chunks.begin()应该告诉原作者,他已经输掉了smartass编码器游戏,毕竟需要一个不起眼的索引:

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

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

最新更新