有没有办法在C++ 17中简化这个C++ 14递归模板?

Omn*_*ous 6 c++ c++14 c++17

poly_eval函数将计算在x的特定值处评估具有特定系数集的多项式的结果.例如,poly_eval(5, 1, -2, -1)计算x ^ 2 - 2x - 1,其中x = 5. constexpr如果给它常量,它将全部如此,它将在编译时计算答案.

它目前使用递归模板在编译时构建多项式求值表达式,并依赖于C++ 14 constexpr.我想知道是否有人能想出一个很好的方法来删除递归模板,也许是使用C++ 17.运行模板的代码使用__uint128_tclang和gcc中的类型.

#include <type_traits>
#include <tuple>

template <typename X_t, typename Coeff_1_T>
constexpr auto poly_eval_accum(const X_t &x, const Coeff_1_T &c1)
{
    return ::std::pair<X_t, Coeff_1_T>(x, c1);
}

template <typename X_t, typename Coeff_1_T, typename... Coeff_TList>
constexpr auto poly_eval_accum(const X_t &x, const Coeff_1_T &c1, const Coeff_TList &... coeffs)
{
    const auto &tmp_result = poly_eval_accum(x, coeffs...);
    auto saved = tmp_result.second + tmp_result.first * c1;
    return ::std::pair<X_t, decltype(saved)>(tmp_result.first * x, saved);
}

template <typename X_t, typename... Coeff_TList>
constexpr auto poly_eval(const X_t &x, const Coeff_TList &... coeffs)
{
    static_assert(sizeof...(coeffs) > 0,
                  "Must have at least one coefficient.");
    return poly_eval_accum(x, coeffs...).second;
}

// This is just a test function to exercise the template.
__uint128_t multiply_lots(__uint128_t num, __uint128_t n2)
{
    const __uint128_t cf = 5;
    return poly_eval(cf, num, n2, 10);
}

// This is just a test function to exercise the template to make sure
// it computes the result at compile time.
__uint128_t eval_const()
{
    return poly_eval(5, 1, -2, 1);
}
Run Code Online (Sandbox Code Playgroud)

另外,我在这里做错了吗?

--------对答案的评论--------

下面有两个很好的答案.一个是清晰和简洁的,但可能无法处理涉及复杂类型(表达树,矩阵等等)的某些情况,尽管它做得很好.它还依赖于有些模糊的操作员.

另一个是不那么简洁,但仍然比我原来的递归模板更清晰,它也处理类型.它扩展为'cn + x*(cn-1 + x*(cn-2 ...',而我的递归版本扩展为cn + x * cn-1 + x * x * cn-2 ....对于大多数合理类型,它们应该是等价的,并且可以轻松修改答案以扩展我的递归扩展到了什么.

我选择了第一个答案,因为它是第一个,它的简洁性更符合我原始问题的精神.但是,如果我要选择一个版本进行制作,我会选择第二个版本.

max*_*x66 7

使用逗号运算符的功能(显然是C++ 17折叠),我想你可以写poly_eval()如下

template <typename X_t, typename C_t, typename ... Cs_t>
constexpr auto poly_eval (X_t const & x, C_t a, Cs_t const & ... cs)
 {
   ( (a *= x, a += cs), ..., (void)0 );

   return a;
 }
Run Code Online (Sandbox Code Playgroud)

扔掉了poly_eval_accum().

观察第一个系数是否已解释,因此您也可以删除static_assert()并通过复制传递,并成为累加器.

- 编辑 -

加入的替代版本来解决使用返回类型的问题std::common_type一个decltype()表达式,因为OP建议; 在这个版本a中再次是一个恒定的参考.

template <typename X_t, typename C_t, typename ... Cs_t>
constexpr auto poly_eval (X_t const & x, C_t const & c1, Cs_t const & ... cs)
 {
   decltype(((x * c1) + ... + (x * cs))) ret { c1 };

   ( (ret *= x, ret += cs), ..., (void)0 );

   return ret;
 }
Run Code Online (Sandbox Code Playgroud)

- 编辑2 -

额外答案:使用逗号运算符(再次)的功能并初始化未使用的C样式整数数组也可以避免C++ 14中的递归

template <typename X_t, typename C_t, typename ... Cs_t>
constexpr auto poly_eval (X_t const & x, C_t const & a, Cs_t const & ... cs)
 {
   using unused = int[];

   std::common_type_t<decltype(x * a), decltype(x * cs)...>  ret { a };

   (void)unused { 0, (ret *= x, ret += cs)... };

   return ret;
 }
Run Code Online (Sandbox Code Playgroud)

  • @oisyn - 不,不是乘以`x`因为`sizeof ...(cs)== 0`; fold表达式变为`((void)0);` (3认同)
  • @Omnifarious - 据我所知,逗号**运算符**是一个序列点; 逗号的序列点问题是用来分隔调用函数的参数; 在这种情况下,未定义评估顺序,但在这种情况下,逗号是分隔符,而不是运算符. (2认同)