我正在自学习如何使用迭代器创建泛型函数。作为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> > >)’
谢谢!
答案 0 :(得分:3)
您可以使用iterator_traits<It>::difference_type
而不是int来确保它不会溢出。这是std::distance返回的类型。
您的编译错误是因为编译器无法确定类型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;
}
答案 1 :(得分:2)
我的第一个问题是:使用
int
计数器是否正常,如果数据太长会溢出吗?
没关系,除非你想支持超过20亿件物品的清单。
当我编译它时,我收到错误:
模板特化无法推断出返回类型T.您必须使用所有模板参数调用它。
mean<vector<int>::const_iterator, double>( v_int.begin(), v_int.begin() )
BTW,在STL中,accumulate()
和distance()
函数已经可以计算总和和计数。
#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;
}
答案 2 :(得分: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 summation algorithm可减少舍入误差(有许多此类算法,其中一些完全消除它)。 / p>