5

私の C++ クラス (Boost はまだカバーしていません) の演習として、STL コンテナーで数値を合計するための 2 つのイテレーターを受け入れるテンプレート化されたメソッドを作成するのに問題があります。
次の例を検討してください。

#include <iostream>
#include <iterator>
#include <vector>

template<typename T>
double Sum(const T & c) {
    return 42.0;    // implementation stubbed
}

// need help writing this method signature to accept two iterators
template<typename T>
double Sum(const typename T::const_iterator & begin,
           const typename T::const_iterator & end) {
    return 43.0;    // another implementation stub
}

int main() {
    std::vector<double> v;
    v.push_back(3.14);
    v.push_back(2.71);
    v.push_back(1.61);    // sums to 7.46

    std::cout << Sum(v) << ' '              // line 23
              << Sum(v.begin(), v.end())    // line 24
              << '\n';
}

このコードは を出力42 43するはずですが、コンパイルに失敗します。
g ++が私に与えるエラーは次のとおりです。

test_exercise2.cpp: In function ‘int main()’:
test_exercise2.cpp:24: error: no matching function for call to ‘Sum(__gnu_cxx::__normal_iterator<double*, std::vector<double, std::allocator<double> > >, __gnu_cxx::__normal_iterator<double*, std::vector<double, std::allocator<double> > >)’

24 行目をコメントアウトすると42、期待どおりの出力が得られます。
2番目のテンプレート化されたメソッドが存在するかどうかに関係なく同じエラーメッセージが表示されるため、何らかの理由で、24行目の呼び出しを私が書いた2番目のメソッドに解決できません。

2 つの反復子を受け入れるメソッドには、どのシグネチャが必要ですか?


私がこれに固執している理由は、 の 2 番目の要素の合計をサポートする必要があるためですstd::map<K, V>。これには、反復子->secondを逆参照する代わりに、さらに 2 つのオーバーロードを呼び出す必要があり
ます 。template<typename K, typename V> double Sum(const std::map<K, V> & m);

std::mapのイテレータの受け渡しを指定する方法がわかればのメソッドが書けるような気がします。template-templates を使用するソリューションには問題ありません。std::liststd::map


編集: 問題の正確な文言 (貢献しない文を省略)。
「前の演習」のコンテナはstd::vector<double>std::list<double>std::map<std::string, double>でした。

テンプレート引数 T を入力として受け取り、double を返す Sum() というテンプレート関数を作成します。テンプレート引数はコンテナになります。

  • 実装では、最後に反復子 (T::const_iterator) を取得します。次に、コンテナー T を反復処理してすべての値を追加するループを作成します。最後に合計を返します。
  • メイン プログラムで、前の演習とは異なるコンテナーに対して Sum() 関数を呼び出します。

作成された Sum() 関数は、コンテナ全体の合計を計算します。また、2 つのイテレータ間の合計を計算する Sum() 関数も作成します。次に、関数は反復子の型にテンプレート引数を使用し、開始反復子と終了反復子の 2 つの反復子を受け入れます。

4

3 に答える 3

7

あなたはこれを過度に複雑にしています。任意のタイプのイテレータのペアが必要ですか? まあ、それは..任意の型の 2 つの引数と同じくらい簡単です。

template<typename Iterator>
double Sum(const Iterator& begin,
           const Iterator& end) {
    return 43.0;    // another implementation stub
}

問題が解決しました。

ところで、C++ 標準ライブラリからヒントを得てください。イテレータを逆参照できない場合は、イテレータから値を取得する関数をユーザーに提供してもらいます。std::map明日も明後日もあり、あらゆる種類の楽しみがあるので、std::unordered_map特別boost::multimapなケースは避けてください。また、値ではなくからキーを合計してほしいとしたらどうでしょうか。std::map

ハードコードされたケースはもう少し複雑です。から来なければならないイテレータのペアstd::map?明示的なテンプレート引数なしで可能かどうかさえわかりません。

template<typename K, typename V, typename Comp, typename Alloc>
double Sum(
    const std::map<K, V, Comp, Alloc>& map
) { ... }

インスタンス化する必要があると具体的に述べたことに注意してくださいstd::map。これにより、コンパイラはパラメータを推測できます。ここから、反復子にアクセスできます。

于 2012-08-31T03:47:02.473 に答える
6

DeadMG が言ったように、簡単な方法はイテレータの型をテンプレート化することです。一方、一般的な規則は、イテレータを値で渡すことです。

template <typename Iterator>
double Sum( Iterator begin, Iterator end );

元のコードが機能しなかった理由については、問題はコンテナーの型が推測できないことです。

template <typename T>
double Sum( T::const_iterator begin, T::const_iterator end );
Sum( v.begin(), v.end() );        // [*] Assume v == const std::vector<double>&

コンパイラが引数の型を推測しようとすると、iteratorであるandSumによって返される型のみが表示されます。その型から、コンテナの型を推測することはできません。型が何であるかを判断できるようにするには、すべての非テンプレート型をテストし、テンプレート型の無限の可能なインスタンス化をすべて生成して、型と一致するネストされた型があるかどうかを調べる必要があります。それを達成することは不可能であるため、言語はそもそもそれを禁止しています。v.begin()v.end()Tconst_iteratorv.begin()v.end()

それを超えて、コメント[*]に関連して、型が推定可能であっても、式が後でどのように使用されるかではなく、関数への引数に対してオーバーロードの解決が実行されます。あなたのプログラムでは、への引数.begin()std::vector<double>非 const左辺値です。これは const ではないため、選択されたオーバーロードは非 const イテレータを生成します (呼び出したい関数内であっても、それを読み取る必要はありません)。

于 2012-08-31T03:58:46.463 に答える
5

std::listイテレータを from などと対比するときの際立った特徴std::mapは、後者がペア型を持っていることvalue_typeです。つまり、与えられたstd::map<K, V>場合、std::map<K, V>::value_typestd::iterator_traits<std::map<K, V>::iterator>::value_typestd::pair<const K, V>です。

したがって、Sumテンプレートは任意のイテレータを受け入れることをお勧めしますが、イテレータから指定された要素 (つまり*it) ではなく、代わりに 'view':で動作することをお勧めしますelement(*it)elementこれで、ペアに直面したときに「正しいことをする」ように注意することができます。

ヒントとして、次のように宣言できSumます (戻り値の型を正しく取得するためのメタプログラミングが少し必要です)。

namespace result_of {

// Second template parameter is an implementation detail
template<
    typename Iterator
    , typename ValueType = typename std::iterator_traits<Iterator>::value_type
>
struct Sum {
    // general case: sum over the value type directly
    typedef ValueType type;
};

// If an iterator admits an std::pair as its value_type then we end up here
template<typename Iterator, typename Key, typename Value>
struct Sum<Iterator, std::pair<Key, Value> > {
    // special case: sum over the second type of the value
    typedef Value type;
};

} // result_of

template<typename Iterator>
typename result_of::Sum<Iterator>::type Sum(Iterator begin, Iterator end);
于 2012-08-31T04:30:40.163 に答える