可变参数模板分隔符

时间:2015-10-30 17:00:12

标签: c++ templates c++11 variadic

我有以下小的可变参数模板,它应该划分一系列数字。据我了解,这是一个粗略的草图,如果我打电话给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();
}

3 个答案:

答案 0 :(得分:11)

divide的实施基本上扩展为以下内容:

divide(20, 2, 2) - &gt; return 20 / divide(2,2) - &gt; 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将包含一个新的https://www.parse.com/apps/quickstart?app_id=iraven#parse_push/ios/swift/existing语法,允许你这样编写它:

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

对于尺寸为1的参数包,它将自动执行The Right Thing™。

答案 1 :(得分:0)

能够使用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

答案 2 :(得分:0)

正如@ 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<>());
}