Ivaylo Valchev
Ivaylo Valchev

Reputation: 10425

Variadic template divider

I have the following little variadic template which is supposed to divide a series of numbers. As I understand it, this is a rough sketch of what would happen if I call divide(20, 2, 2) -> 20/ 2 / 2. Apparently it doesn't happen that well, as the answer I get is 20... It works fine when there are only two arguments.

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

Upvotes: 2

Views: 202

Answers (3)

Snowhawk
Snowhawk

Reputation: 672

As @melak47 noted, you ended up folding-right in your recursive calls. To fold-left, evaluate and forward the result to the remaining elements of your list. If you wanted to do the folding without recursion, you could also just expand the variadic (guaranteed to be in order) and run the values through 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<>());
}

If you want to avoid repeated divisions, you can also just fold the divisors through multiplication then divide.

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

Upvotes: 0

melak47
melak47

Reputation: 4850

Your implementation of divide basically expands to the following:

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

You'll either want to divide from left to right like so:

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...);
}

or multiply the rest of the divisors as suggested in the comments above.

As a side node, C++17 will include a new fold expression syntax, which would allow you to write it like this:

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

Which will do The Right Thing™ for parameter packs of size 1 automagically.

Upvotes: 11

R Sahu
R Sahu

Reputation: 206717

Being able to use cout or printf to understand the flow of execution is very helpful in diagnosing the problem. Below is a lazy man's way of instrumenting your code to diagnose the problem.

#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;
}

Output:

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

Upvotes: 0

Related Questions