Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to have a templated, single-parameter sum function in C++?

In python, we can do this:

int_list = [1, 2, 3, 4, 5]
print(sum(int_list)) # prints 15
float_tuple = (1.2, 3.4, 9.9)
print(sum(float_tuple)) # prints 14.5

The sum function takes any iterable of elements that know how to be added to each other and to 0 and produces the sum.

I wanted to make an identical function in C++11. I'm aware that there exists the accumulate method, but I want a function that takes a single parameter. Essentially, I want to know how to make the following code compile:

#include <string>
#include <iostream>
#include <vector>
#include <deque>
#include <list>

template<typename iterable>
auto sum(iterable iterable_) {
    auto it = iterable_.begin();
    auto end = iterable_.end();
    if (it == end) {
        return 0;
    }
    auto res = *(it++); 
    while (it != end) {
        res += *it++;
    }
    return res;
}

int main() {
    std::vector<int> int_vector = {0, 1, 2, 3, 4, 5}; 
    std::cout << sum(int_vector) << '\n';    // prints 15
    std::deque<int> int_deque = {4, 5, 7};
    std::cout << sum(int_deque) << '\n';     // prints 16
    std::list<float> float_list = {1.2, 3.4, 9.9};
    std::cout << sum(float_list) << '\n';    // should print 14.5, but produces error.

}

This code almost works. The issue is that auto sees the return 0; in the case that the iterable is empty and it assumes that the function must return an int. Then it sees that the float version returns a float and it gets confused. Is there any way to tell the compiler to, say return float(0) if it sees that the return later on returns float?

like image 239
Jon McClung Avatar asked Dec 08 '25 07:12

Jon McClung


1 Answers

Yes, you can make this work at least for standard containers.

A standard container defines a type alias named value_type for the type of value stored in that container. For an empty container, you can return a value-constructed object of this type:

template<typename iterable>
auto sum(iterable const &iterable_) {
    auto it = iterable_.begin();
    auto end = iterable_.end();
    if (it == end) {
        return typename iterable::value_type();
    }
    auto res = *(it++); 
    while (it != end) {
        res += *it++;
    }
    return res;
}

This does depend on the contained type being default-constructible, but that's probably not a major problem (certainly works for primitive types like int and float).

like image 58
Jerry Coffin Avatar answered Dec 09 '25 19:12

Jerry Coffin