Czy boost ma typ danych dla operacji set, który jest prostszy niż STL?

Uważam, żemetoda C++ STL wykonywania prostych operacji setowych jest dość niewygodna w użyciu. Na przykład, aby znaleźć różnicę między dwoma zbiorami:

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;

Czy boost oferuje alternatywny zestaw klas, które zredukowałyby powyższy przykład do czegoś takiego:

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

(podobny do QSet w Qt, który w ten sposób nadpisuje operator-.)

Author: Community, 2013-02-26

3 answers

Zobacz algorytmy Boost Range Set . Nadal jednak oczekują iteratora wyjściowego.

 11
Author: Maxim Egorushkin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-26 13:24:16

Nie. Ale tutaj jest jak to posprzątać.

Najpierw przepisz funkcje oparte na iteratorze jako funkcje dystansowe. To o połowę zmniejszy Twój kocioł.

Po drugie, niech zwracają konstruktory kontenerów zamiast iteratorów insert: daje to efektywną składnię przypisywania.

Po Trzecie, i prawdopodobnie za daleko, napisz je jako nazwane operatory.

Ostateczny wynik to:

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

... po napisaniu boatload of boilerplate code gdzie indziej.

Z tego co wiem, boost robi krok 1.

Ale każdy z powyższych trzech etapów powinien znacznie zmniejszyć płytkę kotła.

Konstruktor kontenera:

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_) {}
};

Który wymaga napisania is_back_insertable (dość standardowe SFINAE).

Owijasz swój funktor (lub oparty na iteratorze), który pobiera back_insert_iterator jako ostatni argument i używasz std::bind, aby powiązać parametry wejściowe, pozostawiając ostatni wolny. Następnie przekaż to container_builder i zwróć to.

container_builder może być wtedy domyślnie rzucany do dowolnego kontenera, który akceptuje std::back_inserter (lub ma swój własny ADL back_inserter), a semantyka move na każdym kontenerze std sprawia, że construct-then-return jest dość wydajny.

Oto moja tuzin linii nazwanych biblioteką operatorów:

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) );
  }
}

Przykład na żywo używanie go do implementacji vector *concat* vector. Obsługuje tylko jednego operatora, ale jego rozszerzenie jest łatwe. W przypadku poważnego użycia radzę mieć times funkcję, która domyślnie wywołuje invoke dla *blah*, an add for +blah+ that does the same, etc. <blah> może bezpośrednio zadzwonić invoke.

Wtedy programista klienta może przeciążać specyficzne dla operatora przeciążenie i to działa, lub ogólne invoke.

Oto podobna biblioteka używana do implementacji *then* zarówno na funkcje zwracające krotki, jak i futures.

Oto prymitywne *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;

Przykład na żywo .

 77
Author: Yakk - Adam Nevraumont,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:22

Nie i myślę, że nigdy nie ma czegoś takiego, jest to ogólna zasada w C++ , że gdy możesz mieć funkcję nie-członkowską do wykonania zadania, nigdy nie rób z niej członka. więc nie może tak być, ale może być Boost::Range help you.

 3
Author: BigBoss,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-26 13:25:27