代码之家  ›  专栏  ›  技术社区  ›  recipriversexclusion

编写简单的STL泛型函数时出现问题

  •  1
  • recipriversexclusion  · 技术社区  · 15 年前

    我正在自学如何使用迭代器创建泛型函数。作为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 对于计数器OK,如果数据太长是否会溢出?

    我从以下测试线束调用我的函数:

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

    谢谢!

    3 回复  |  直到 15 年前
        1
  •  3
  •   Tomaka17    15 年前
    1. 你可以用 iterator_traits<It>::difference_type std::distance .

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

    这是因为编译器首先只查看函数的声明。如果你只看声明,你就不知道t是什么。与第一个问题一样,您可以使用迭代器特性。

    你可以这样做:

    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;
    }
    
        2
  •  2
  •   kennytm    15 年前

    我的第一个问题是:使用 int

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

    当我编译这个时,我得到一个错误:

    模板专门化无法推断返回类型T。必须使用所有模板参数调用它。

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

    顺便说一句,在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;
    }
    
        3
  •  2
  •   Alexandre C.    15 年前

    这应该作为注释,但是注释不允许格式化代码。我认为这至少应该是一种教育。请参阅 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()) .

    它起作用是因为 每个人 返回已按顺序应用于所有元素的函子。在我们的例子中,函子把结果累加起来,我们就可以得到平均值。

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