可变参数模板分隔符

Variadic template divider

我有以下小的可变模板,它应该划分一系列数字。据我了解,这是我调用 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 expression 语法,允许您这样写:

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

这将自动对大小为 1 的参数包执行 The Right Thing™。

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

#include <iostream>

template<class first_t>
double divide(const first_t &first)
{
   std::cout << "Came to 1\n";
   return first;
}

template<class first_t, class... rest_t>
double divide(const first_t &first, const rest_t&... rest)
{
   std::cout << "Came to 2\n";
   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<>());
}