C++的"yield"关键字,如何从函数中返回迭代器?

moo*_*eep 20 c++ iterator generator yield-keyword coroutine

请考虑以下代码.

std::vector<result_data> do_processing() 
{
    pqxx::result input_data = get_data_from_database();
    return process_data(input_data);
}

std::vector<result_data> process_data(pqxx::result const & input_data)
{
    std::vector<result_data> ret;
    pqxx::result::const_iterator row;
    for (row = input_data.begin(); row != inpupt_data.end(); ++row) 
    {
        // somehow populate output vector
    }
    return ret;
}
Run Code Online (Sandbox Code Playgroud)

当我在考虑是否可以期待返回值优化(RVO)时,我发现Jerry Coffin的这个答案 [强调我的]:

至少IMO,这通常是一个糟糕的主意,但不是出于效率原因.这是一个糟糕的主意,因为有问题的函数通常应该写成通过迭代器生成输出的通用算法.几乎任何接受或返回容器而不是在迭代器上运行的代码都应该被认为是可疑的.

不要误解我的意思:有时候传递类似于集合的对象(例如字符串)是有意义的,但对于引用的例子,我会考虑传递或返回向量一个糟糕的想法.

有一些Python背景,我非常喜欢Generators.实际上,如果它是Python,我会把上面的函数写成一个Generator,即避免在其他任何事情发生之前处理整个数据的必要性.例如这样:

def process_data(input_data):
    for item in input_data:
        # somehow process items
        yield result_data
Run Code Online (Sandbox Code Playgroud)

如果我正确地解释了Jerry Coffins的说法,这就是他的建议,不是吗?如果是这样,我如何在C++中实现它?

Kon*_*lph 16

不,这不是杰瑞的意思,至少不是直接的.

yield在Python中实现协同程序.C++没有它们(但它们当然可以被模拟,但如果干净利落就有点参与其中).

但是Jerry的意思只是你应该传入一个输出迭代器,然后写入:

template <typename O>
void process_data(pqxx::result const & input_data, O iter) {
    for (row = input_data.begin(); row != inpupt_data.end(); ++row)
        *iter++ = some_value;
}
Run Code Online (Sandbox Code Playgroud)

并称之为:

std::vector<result_data> result;
process_data(input, std::back_inserter(result));
Run Code Online (Sandbox Code Playgroud)

我不相信这通常比返回矢量更好.

  • 支持传递'out'迭代器的一个很大的优点是,当调用`process_data`*两次* - 可能在不同的输入数据上时,代码变得更有效 - 但是希望输出在同一个`result`对象中.效率就是你首先使用C++的原因,对吧?除此之外,您允许调用者为容器使用自定义分配器. (3认同)

Tem*_*Rex 12

Boost.Asio作者Chris Kohlhoff撰写了一篇博文,内容如下:http://blog.think-async.com/2009/08/secret-sauce-revealed.html

yield用宏来模拟

#define yield \
  if ((_coro_value = __LINE__) == 0) \
  { \
    case __LINE__: ; \
    (void)&you_forgot_to_add_the_entry_label; \
  } \
  else \
    for (bool _coro_bool = false;; \
         _coro_bool = !_coro_bool) \
      if (_coro_bool) \
        goto bail_out_of_coroutine; \
      else
Run Code Online (Sandbox Code Playgroud)

这必须与一个coroutine类一起使用.有关详细信息,请参阅博客.