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. intの代わりに使用iterator_traits<It>::difference_typeして、オーバーフローしないようにすることができます。これは、 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など、より複雑な加算スキームをサポートするように拡張できることに注意してください(このようなアルゴリズムは多数あり、その一部は完全に排除されます)。

于 2010-07-22T16:03:26.747 に答える