1

我正在自学如何使用迭代器创建通用函数。作为 Hello World 步骤,我编写了一个函数来获取给定范围内的平均值并返回值:

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

我的第一个问题是:使用int计数器可以吗,如果数据太长会溢出吗?

我从以下测试工具中调用我的函数:

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

当我编译这个我得到错误:

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> > >)’

谢谢!

4

3 回答 3

3
  1. 您可以使用iterator_traits<It>::difference_type而不是 int 来确保它不会溢出。这是std::distance返回的类型。

  2. 您的编译错误是因为编译器无法确定类型 T

这是因为编译器首先只查看函数的声明。如果你只看声明,你无法知道 T 是什么。与第一个问题一样,您可以使用 iterator_traits。

你可以这样:

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;
}
于 2010-07-22T15:57:41.143 回答
2

我的第一个问题是:使用int计数器可以吗,如果数据太长会溢出吗?

没关系,除非您想支持超过 20 亿个项目的列表。

当我编译这个我得到错误:

模板特化无法推导出返回类型 T。您必须使用所有模板参数调用它。

mean<vector<int>::const_iterator, double>( v_int.begin(), v_int.begin() )

顺便说一句,在 STL 中,accumulate()anddistance()函数已经可以计算总和和计数。

#include <numeric>
#include <iterator>

template <class It, class T>
T mean (It begin, It end) {
  if (begin == end)
    throw domain_error("mean called with empty array");

  T zero = 0;
  T sum = std::accumulate(begin, end, zero);
  typename iterator_traits<It>::difference_type count;
  count = std::distance(begin, end);
  return sum / count;
}
于 2010-07-22T15:56:54.000 回答
2

这应该作为注释,但注释不允许格式化代码。我认为这至少应该是教学上的。请参考 和 的for_each文档unary_function

我会这样写:

#include <algorithm>
#include <functional>
#include <iterator>

template <typename T>
struct accum : std::unary_function<T, void>
{
    void operator()(T const& x)
    { count++; acc += x; }

    // Edited to take care of case count == 0
    T mean() const
    {
        if (count) return acc / count; 
        else throw "Division by zero";
    }

private:
    size_t count;
    T acc;
};


template <template Iter>
typename std::iterator_traits<Iter>::value_type
mean(Iter begin, Iter end)
{
    typedef typename std::iterator_traits<Iter>::value_type T;
    return std::for_each(begin, end, accum<T>()).mean();
}

用法:mean(v.begin(), v.end())

它之所以有效,是因为for_each返回了已按顺序应用于所有元素的函子。在我们的例子中,函子累积了结果,我们可以检索平均值。

请注意,accum可以增强函子以支持更详细的求和方案,例如减少舍入误差的Kahan 求和算法(有许多这样的算法,其中一些完全消除了它)。

于 2010-07-22T16:03:26.747 回答