29

単純なセット操作を行うC++ STL メソッドは、非常に扱いにくいと思います。たとえば、2 つのセットの違いを見つけるには、次のようにします。

std::set<int> newUserIds;
set_difference(currentUserIds.begin(), currentUserIds.end(), mPreviousUserIds.begin(), mPreviousUserIds.end(), std::inserter(newUserIds, newUserIds.end()));
std::set<int> missingUserIds;
set_difference(mPreviousUserIds.begin(), mPreviousUserIds.end(), currentUserIds.begin(), currentUserIds.end(), std::inserter(missingUserIds, missingUserIds.end()));
mPreviousUserIds = currentUserIds;

ブーストは、上記の例を次のように縮小するクラスの代替セットを提供しますか?

set_type<int> newUserIds = currentUserIds.difference(mPreviousUserIds);
set_type<int> missingUserIds = mPreviousUserIds.difference(currentUserIds);

(この方法でオーバーライドする Qt のQSetoperator-に似ています。)

4

3 に答える 3

89

いいえ。しかし、私はここでそれをきれいにする方法です。

まず、イテレータベースの関数を範囲ベースの関数として書き直します。これにより、定型文が半分になります。

次に、挿入イテレータを使用するのではなく、コンテナビルダーを返すようにします。これにより、効率的な割り当て構文が得られます。

第三に、おそらくあまりにも遠いので、それらを名前付き演算子として記述します。

最終的な結果は次のとおりです。

set<int> s = a *intersect* b;
set<int> s2 = c -difference- s;
set<int> s3 = a *_union_* (b *intersect* s -difference- s2);

...ボイラープレートコードのボートロードを他の場所に書き込んだ後。

私の知る限り、ブーストはステップ1を実行します。

ただし、上記の3つの段階のそれぞれで、定型文が大幅に削減されます。

コンテナビルダー:

template<typename Functor>
struct container_builder {
  Functor f;
  template<typename Container, typename=typename std::enable_if<back_insertable<Container>::value>::type>
  operator Container() const {
    Container retval;
    using std::back_inserter;
    f( back_inserter(retval) );
    return retval;
  }
  container_builder(Functor const& f_):f(f_) {}
};

これには書き込みが必要is_back_insertableです(かなり標準的なSFINAE)。

back_insert_iteratorを最後の引数として取る範囲ベース(またはイテレーターベース)のファンクターをラップし、最後のstd::bindパラメーターを空けたまま入力パラメーターをバインドするために使用します。次に、それをに渡し、それcontainer_builderを返します。

container_builderstd::back_inserter次に、受け入れる(または独自のADLを持つback_inserter)任意のコンテナに暗黙的にキャストでき、すべてのコンテナのmoveセマンティクスstdにより、construct-then-returnが非常に効率的になります。

これが、operatorlibraryという名前の私のダース行です。

namespace named_operator {
  template<class D>struct make_operator{make_operator(){}};

  template<class T, char, class O> struct half_apply { T&& lhs; };

  template<class Lhs, class Op>
  half_apply<Lhs, '*', Op> operator*( Lhs&& lhs, make_operator<Op> ) {
    return {std::forward<Lhs>(lhs)};
  }

  template<class Lhs, class Op, class Rhs>
  auto operator*( half_apply<Lhs, '*', Op>&& lhs, Rhs&& rhs )
  -> decltype( named_invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) ) )
  {
    return named_invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) );
  }
}

を実装するためにそれを使用する実例vector *concat* vector。1人のオペレーターのみをサポートしますが、拡張は簡単です。times本格的に使用する場合は、デフォルトでを呼び出す関数、同じことを行うinvokeforなど が直接呼び出すことができる関数を用意することをお勧めします。*blah*add+blah+<blah>invoke

次に、クライアントプログラマーは、オペレーター固有のオーバーロードをオーバーロードでき、それが機能しinvokeます。

*then*これは、タプルを返す関数と先物の両方に実装するために使用されている同様のライブラリです。

これがプリミティブ*in*です:

namespace my_op {
  struct in_t:named_operator::make_operator<in_t>{};
  in_t in;

  template<class E, class C>
  bool named_invoke( E const& e, in_t, C const& container ) {
    using std::begin; using std::end;
    return std::find( begin(container), end(container), e ) != end(container);
  }
}
using my_op::in;

ライブ例

于 2013-02-26T13:47:20.073 に答える
12

ブースト範囲セットのアルゴリズムを参照してください。ただし、出力イテレータはまだ必要です。

于 2013-02-26T13:24:16.773 に答える
3

いいえ、そのようなことは決してないと思います。これはC++の一般原則であり、非メンバー関数を使用してジョブを実行できる場合、その関数をメンバーにしないでください。そんなわけにはいきませんが、Boost::Rangeが役に立ちます。

于 2013-02-26T13:25:27.087 に答える