2

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

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

然而该代码无法编译,我对代码进行了一些修改以使其编译,但我遇到了 2 个问题:

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

6 回答 6

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。

于 2008-10-23T19:15:17.973 回答
2

我认为你认为最好的办法就是保持代码不变是正确的。如果您在(a)您正在编写它并且(b)您理解您要解决的确切问题时很难理解它,想象一下当有人在 3 年后出现并且必须了解您编写的问题和解决方案。

于 2008-10-22T07:32:08.690 回答
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;
}
于 2008-10-22T15:02:33.663 回答
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));
于 2008-10-22T07:19:14.673 回答
1

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

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

于 2008-10-22T08:32:48.410 回答
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 成员函数。

于 2008-10-22T18:57:50.957 回答