如何为C++元组编写折叠/求和函数?

tin*_*lyx 6 c++ c++11 stdtuple c++17

我想编写一个fold函数std::tuple来计算例如给定元组中所有元素的总和(或乘积).例如,给定

std::tuple<int,double> t = std::make_tuple(1,2);
Run Code Online (Sandbox Code Playgroud)

我想算一下

auto s = sumT(t); //giving 3
Run Code Online (Sandbox Code Playgroud)

我试过但是无法在下面编译我的模板编程(c ++ 11/1z)代码.我也试着去适应接受的答案对我的其他问题(如何执行用C元组算术++(C++ 11/C++ 17)? ),但无法弄清楚如何使用std::index_sequence在这种情况下.

我遇到的问题是:

1)我无法弄清楚类型,例如如何使用第一个元素的类型作为返回类型.目前,我_res在模板中使用了一个类型,但我不知道是否会阻止c ++的自动类型推理.

2)我想在不使用显式初始元素的情况下对其进行编程0,以便可以将其用于其他类型的fold操作.

目前,递归在最后一个元素处结束.我想结束递归,_size - 1以便我可以直接对最后一个元素执行操作而不需要求助0.

我下面的代码试图通过递归来做到这一点.但我不熟悉模板编程,以及循环如何为元组工作.

有人可以帮助修复代码或提出更好的解决方案吗?

到目前为止我的代码是:

#include <tuple>
#include <iostream>
#include <functional>

// helper class for fold operations
template<typename Op,typename _res, typename _Tp, size_t _i, size_t _size>
struct _tuple_fold  {
    static constexpr _res _op(Op const & op, const _Tp& _t) {
      return _res(op(std::get<_i>(_t),
                _tuple_fold<Op, _res, _Tp, _i + 1, _size>::_op(op,_t) ));
    }
};

template<typename Op,typename _res,typename _Tp, size_t _size>
struct _tuple_fold<Op, _res,_Tp, _size, _size> {
  static constexpr _res _op(Op const &, const _Tp&) { return 0; }
};

template <typename ... Ts>
auto sumT (std::tuple<Ts...> const & t1)  {
  return _tuple_fold::_op(std::plus<>{}, t1);
}

int main () {
  std::tuple<int,double> t = std::make_tuple(1,2);
  auto s = sumT(t);
  std::cout << s << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

用于编译的错误消息g++ -std=c++17 tuple_sum.cpp:

tuple_sum.cpp: In function ‘auto sumT(const std::tuple<_Elements ...>&)’:
tuple_sum.cpp:21:10: error: ‘template<class Op, class _res, class _Tp, long unsigned int _i, long unsigned int _size> struct _tuple_fold’ used without template parameters
   return _tuple_fold::_op(std::plus<>{}, t1);
          ^
tuple_sum.cpp: In function ‘int main()’:
tuple_sum.cpp:27:19: error: ‘void s’ has incomplete type
    auto s = sumT(t);
                   ^
Run Code Online (Sandbox Code Playgroud)

我不确定如何_tuple_fold在呼叫站点上提供类型参数,尤其是类型std::plus.

Mas*_*nes 16

请注意,在c ++ 17中我们可以应用()折叠:

auto t = std::make_tuple( 1, 2. );
auto sum = std::apply([]( auto... v ){ return ( v + ... ); }, t );
Run Code Online (Sandbox Code Playgroud)

这适用于任何类似元组的类型,并遵循开箱即用的"+"常用促销/转换规则(这可能是也可能不是可取的).在上面我通过值传递,因为我们正在处理算术类型,但你可以应用你喜欢的转发策略...

  • 当您知道并且可以对折叠运算符 + 进行硬编码时,这非常有效。 (2认同)

Bri*_*ian 5

值得庆幸的是,if constexpr在 C++17 中,我们可以通过引入部分专门化的辅助结构来避免使事情复杂化,并且可以轻松地在我们想要的任何条件下终止递归:

#include <functional>
#include <iostream>
#include <tuple>
#include <type_traits>

template <size_t index, class Op, class... Ts>
constexpr auto tuple_fold(Op op, const std::tuple<Ts...>& t) {
    if constexpr(index == sizeof...(Ts) - 1) {
        return std::get<index>(t);
    } else {
        return op(std::get<index>(t), tuple_fold<1 + index>(op, t));
    }
}

template <typename ... Ts>
constexpr auto sumT (std::tuple<Ts...> const & t1)  {
  return tuple_fold<0>(std::plus<>{}, t1);
}

int main () {
  std::tuple<int,double> t = {1, 2.0};
  auto s = sumT(t);
  static_assert(std::is_same_v<decltype(s), double>);
  std::cout << s << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

Coliru 链接:http ://coliru.stacked-crooked.com/a/1e7051b8652fb942

这将执行右折叠,a + (b + (c + ...))但如果您愿意,很容易将其重写为执行左折叠。