2015-10-30 4 views
2

У меня есть следующий небольшой вариационный шаблон, который должен делить серию чисел. Насколько я понимаю, это примерный пример того, что произойдет, если я позвоню divide(20, 2, 2) ->20/ 2/2. По-видимому, это не так хорошо, так как ответ, который я получаю, составляет 20 ... Он отлично работает, когда есть только два аргумента.Variadic template divider

#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(); 
} 
+0

Неудивительно, что вы закончите с делением на обратное. 20 делится на 2, деленный на 2 = 20/1 = 20. назовите его на 20, 2, 1, вы получите 10. – SergeyA

+2

вам нужно 'return first/multiply (rest)' –

+0

'((20/2)/2)! = (20/(2/2)). – Jarod42

ответ

11

Ваша реализация 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 автомагически.

+0

Спасибо за эскиз! Вот что я смутил! – DeiDei

+0

Я действительно вчера прочитал о своём выражении. Не могу дождаться этого! Кланг уже поддерживает его. – DeiDei

0

Способность использовать cout или printf для понимания потока выполнения очень полезно при диагностике проблемы. Ниже представлен способ ленивого человека, который помогает вам диагностировать проблему.

#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 
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<>()); 
} 
Смежные вопросы