16

アルゴリズムを使用する関数オブジェクトを作成するように教えられています。

operator()次のようなを呼び出すアルゴリズムがあります。

  • for_each
  • find_if
  • remove_if
  • 最大要素
  • count_if

これらの関数オブジェクトは通常、unary_functionorから継承して、 functionpredicateなどのbinary_functionように動作する必要があります。

しかし、本は通常、作成の例を示していませんOutputIterators:

たとえば、 のような関数の出力をトラバースするには std::set_intersection()、宛先コンテナーを提供してから、結果をトラバースする必要があります。

std::vector<int> tmp_dest;

std::set_difference (
        src1.begin(), src1.end(), 
        src2.begin(), src2.end(), 
        std::back_inserter(tmp_dest));

std::for_each( tmp_dest.begin(), tmp_dest.end(), do_something );
int res = std::accumulate( tmp_dest.begin(), tmp_dest.end(), 0 );

ただし、次のように、最初に保存せずに各アルゴリズムの値を使用する方が効率的な場合があると考えてください。

std::set_difference (
        src1.begin(), src1.end(), 
        src2.begin(), src2.end(), 
        do_something );

Accumulator accumulate(0);  // inherits from std::insert_iterator ?
std::set_difference (
        src1.begin(), src1.end(), 
        src2.begin(), src2.end(), 
        accumulate );
  • 通常、このAccumulatorのようなクラスを作成する必要がありますか?
  • そのデザインはどのように見えるべきですか?
  • 何から継承する必要がありますか? アキュムレータは から継承できinsert_iteratorますが、実際にはイテレータではありません (たとえば、 を実装していませんoperator++()) 。

広く受け入れられている慣行は何ですか?

4

5 に答える 5

6

受け取った値ごとに独自の関数を呼び出す出力反復子が必要な場合は、Boost.Iterator の function_output_iteratorを使用します。

于 2013-09-19T14:33:49.210 に答える
4

コードがどのように機能し、何をしているのかが将来のメンテナに明確である限り、これに根本的な問題はないと思います。

おそらく、標準クラスからそのような操作を継承することはありません(それを与える以外output_iterator_tag)。テンプレートを扱っているので、親インターフェースを扱う必要はありません。

ただし、ステートメント(eg it does not implement operator++() )が正しくないように思われることに注意してください。「出力反復子」として渡すものは、コピー可能、逆参照から割り当て、インクリメント可能であることを含む出力反復子の要件を満たす必要があります。渡すオブジェクト タイプが何であれ、これらの要件を満たす必要があります。

于 2013-09-19T14:31:52.000 に答える
3

set_differenceこれに対する私の見解は、Boost を使用することです (トピックからは外れますが、Boost Range アルゴリズムのバージョンも示しています)。

#include <set>
#include <boost/range/algorithm.hpp>
#include <boost/function_output_iterator.hpp>
#include <cassert>

void do_something(int) {}

int main()
{
    const std::set<int> 
         src1 { 1,2,3 }, 
         src2 { 1,9 };

    unsigned total = 0;

    boost::set_difference(src1, src2, 
                boost::make_function_output_iterator([&](int i) 
                { 
                    total += i*i; 
                }));

    assert(total == 13); // 2*2 + 3*3
}

Coliruでライブを見る

于 2013-09-19T14:47:08.793 に答える
2

出力反復子を取るアルゴリズムのターゲットは、出力反復子によって表される値のシーケンスです。彼らは次の 2 つの理由で反復子を使用します。

  1. 結果が別の場所に格納される可能性が非常に高く、つまりイテレータが役立ちます。
  2. プロトコルは、各位置が 1 回だけ書き込まれることを義務付けています。これは、関数呼び出しインターフェースよりも制限的です。つまり、追加の保証があります。

一部のアルゴリズムでは、関数呼び出しインターフェイスを備えたバージョンと反復子インターフェイスを備えたバージョンの両方が提供されます。たとえば、それは と の違いstd::for_each()ですstd::copy()

いずれにせよ、出力イテレータが必要な場所で呼び出される関数が必要な場合は、他のイテレータ操作をノーオペレーションにし、 の結果への割り当て時に関数を呼び出すだけです*it。これにより、完全に有効な出力イテレータが作成されます。

于 2013-09-19T15:16:19.917 に答える
1

以下の作品:

#include <cassert>
#include <algorithm>

class AccumulatorIterator
{
public:
    explicit AccumulatorIterator(int initial) : value(initial) {}

    AccumulatorIterator& operator = (int rhs) { value += rhs; return *this; }
    AccumulatorIterator& operator *() { return *this; }

    AccumulatorIterator& operator ++() { return *this; }
    operator int() const { return value; }
private:
    int value;
};

int main() {
    int first[] = {5,10,15,20,25};
    int second[] = {50,40,30,20,10};

    std::sort(std::begin(first), std::end(first));   //  5 10 15 20 25
    std::sort(std::begin(second), std::end(second)); // 10 20 30 40 50

    const int res = std::set_intersection (std::begin(first), std::end(first),
        std::begin(second), std::end(second), AccumulatorIterator(0));

    assert(res == 10 + 20);
    return 0;
}
于 2013-09-19T14:40:22.067 に答える