Search code examples
c++stlgeneric-function

problem writing a simple STL generic function


I'm self-learning how to create generic functions using iterators. As the Hello World step, I wrote a function to take the mean in the given range and returns the value:

// It is the iterator to access the data, T is the type of the data.
template <class It, class T> 
T mean( It begin, It end ) 
{
    if ( begin == end ) {
        throw domain_error("mean called with empty array");
    }

    T sum = 0;
    int count = 0;
    while ( begin != end ) {
        sum += *begin;
        ++begin;
        ++count;
    }
    return sum / count;
}

My first question is: Is using int for the counter OK, can it overflow if the data is too long?

I call my function from the following test harness:

template <class It, class T> T mean( It begin, It end );

int main() {
    vector<int> v_int;
    v_int.push_back(1);
    v_int.push_back(2);
    v_int.push_back(3);
    v_int.push_back(4);

    cout << "int mean    = " << mean( v_int.begin(), v_int.begin() ) << endl;;

    return 0;
}

When I compile this I get the error:

error: no matching function for call to ‘mean(__gnu_cxx::__normal_iterator<int*,    
std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*,
std::vector<int, std::allocator<int> > >)’

Thanks!


Solution

    1. You can use iterator_traits<It>::difference_type instead of int to be sure that it doesn't overflow. This is the type returned by std::distance.

    2. Your compilation error is because the compilator cannot determine the type T

    This is because the compilator looks only at the function's declaration first. And if you look only at the declaration, you can't know what T is. As with the first question, you can use iterator_traits.

    You can what you want like this:

    template <class It> 
    typename std::iterator_traits<It>::value_type mean( It begin, It end )
    {
        if ( begin == end ) {
            throw domain_error("mean called with empty array");
        }
    
        typename std::iterator_traits<It>::value_type sum = 0;
        typename std::iterator_traits<It>::difference_type count = 0;
        while ( begin != end ) {
            sum += *begin;
            ++begin;
            ++count;
        }
        return sum / count;
    }