可变模板除法器

Variadic template divider

本文关键字:除法器      更新时间:2023-10-16

我有下面的小变分模板,它应该用来划分一系列数字。据我所知,这是一个粗略的草图,如果我调用divide(20, 2, 2)->20/ 2 / 2会发生什么。显然,这并没有发生得那么好,因为我得到的答案是20……当只有两个论点时,这很好。

#include <iostream>
template<class first_t>
auto divide(const first_t &first)
{
    return first;
}
template<class first_t, class... rest_t>
double divide(const first_t &first, const rest_t&... rest)
{
    return first / divide(rest...);
}
int main()
{
    std::cout << divide(20, 2, 2); //should print 5
    std::cin.get();
}

divide的实现基本上扩展到以下内容:

divide(20, 2, 2)->return 20 / divide(2,2)->return 20 / 1

你要么想从左到右这样划分:

template<class first_t, class second_t, class... rest_t>
double divide(const first_t& first, const second_t& second, const rest_t&... rest)
{
    return divide(first/second, rest...);
}

或者如上面评论中所建议的那样乘以其余的除数。

作为一个侧节点,C++17将包含一个新的fold表达式语法,这将允许您这样写:

template<class... value_t>
auto divide(const value_t&... values) {
    return (... / values); 
    // and (values / ...) would replicate your original implementation :)
}

哪个会做正确的事™对于自动大小为1的参数包。

能够使用coutprintf来理解执行流程对诊断问题非常有帮助。下面是一个懒人的方法来检测你的代码来诊断问题。

#include <iostream>
template<class first_t>
double divide(const first_t &first)
{
   std::cout << "Came to 1n";
   return first;
}
template<class first_t, class... rest_t>
double divide(const first_t &first, const rest_t&... rest)
{
   std::cout << "Came to 2n";
   auto res = divide(rest...);
   std::cout << "res: " << res << "n";
   return 1.0*first / res;
}
int main()
{
   std::cout << divide(20, 2, 2) << std::endl;
   return 0;
}

输出:

Came to 2
Came to 2
Came to 1
res: 2
res: 1
20

正如@melak47所指出的,您最终在递归调用中向右折叠。要向左折叠,请评估结果并将其转发到列表的其余元素。如果你想在没有递归的情况下进行折叠,你也可以只扩展变量(保证有序),并通过std::accumulate运行值。

template <typename... Values>
double divide(double dividend, Values... divisors) {
  std::initializer_list<double> div_list = {double(divisors)...};
  return std::accumulate(std::begin(div_list), std::end(div_list), dividend,
                         std::divides<>());
}

如果你想避免重复除法,你也可以通过乘法将除数相乘,然后再除法。

template <typename... Values>
double divide(double dividend, Values... divisors) {
  std::initializer_list<double> div_list = {double(divisors)...};
  return dividend / std::accumulate(std::begin(div_list), std::end(div_list),
                                    1.0, std::multiplies<>());
}