可能返回值也可能不返回值的函数的迭代器缩减器模式

Iterator-reducer pattern for functions that may or may not return a value

本文关键字:返回值 模式 迭代器 函数 也可能      更新时间:2023-10-16

以下函数对每个元素应用一个函子并减少返回值:

template <class FCT, class RED>
RED::TYPE forAllElements(FCT functor, RED reducer){
  for(/* all elem in elements */){
    reducer(functor(elem));
  }
  return reducer.value;
}

现在,有时我可能希望只对所有元素调用functor,而不减少任何内容。基本上,我想有这样的东西:

class FunctorThatReturnsNothing{
  void operator() (Elem e){
    // do something, return nothing...
  }
}
class DummyReducer{
  using TYPE = void; // ??? something like that ???
  template <class FCT>
  void operator() (/* ??? what here */){
    // do nothing...
  }
}
forAllElements(FunctorThatReturnsNothing(), DummyReducer());

但这不会编译,因为我有reducer(functor(elem)),其中void函数的不存在的返回值被当作参数。

有没有一种方法可以让它在不复制void和非void情况下的forAllElements的情况下适用于void函子

(对于怀疑XY问题的人:我基本上知道迭代和减少的不同方法,我认为所提出的回调方法适合我的情况。我只是想知道如何避免"返回值+减少"answers"只是回调"的情况出现重复代码。)

我认为您可以只创建一个VoidReducer类,但您需要的不是return reducer.value;,而是return reducer.getvalue();。然后您只需制作void VoidReducer::getvalue(){}

我还没有对此进行测试,但这个想法应该可行。如果f和当前函数都具有返回类型void,则允许使用return f();

编辑
现在我更仔细地阅读了这个问题,我发现你问的问题是reducer(functor(elem));
为此,我将基于decltype(functor(elem))编译时间调度。

template <class Functor, class Reducer, class Elem>
void Combine(Functor functor, Reducer & reducer, Elem elem, std::true_type)
{
  functor(elem);
}
template <class Functor, class Reducer, class Elem>
void Combine(Functor functor, Reducer & reducer, Elem elem, std::false_type)
{
    reducer(functor(elem));
}
template <class Functor, class Reducer, class Elem>
void Combine(Functor functor, Reducer & reducer, Elem elem)
{
    Combine(functor, reducer, elem, std::is_same<decltype(functor(elem)), void>());
}

那么调用Combine而不是reducer(functor(elem))将正确地减少functor的返回值,当且仅当它不是void时。

附言:撒上参考文献和std::forward调用来品尝。

如果你仍然想使用forAllElements函数,而直接使用std::for_each,你可以创建一个不带减缩器的函数重载,只需在内部使用std::for_each

template <class FCT>
void forAllElements(FCT functor){
    std::for_each(std::begin(...), std::end(...), functor);
}
...
forAllElements(FunctorThatReturnsNothing());

如果无法为容器获取"迭代器"(如果使用非标准容器或指针),当然可以有自己的循环,只需调用functor

一种干净而灵活的方法是返回函数对象本身,而不是硬编码的value字段。

template <class FCT, class RED>
RED forAllElements(FCT functor, RED reducer){
  for(/* all elem in elements */){
    reducer(functor(elem));
  }
  return reducer;
}

然后,操作可以没有返回值,甚至可以有多个返回值,并带有信息性名称。

您的原始方法可以看作是对这个基本组件的方便包装:

template <class FCT, class RED>
RED::TYPE forAllElements_convenience(FCT functor, RED reducer){
  return forAllElements(functor, reducer).value;
}