Ответ 1
См. Алгоритмы настройки набора высоты. Они все еще ожидают выходного итератора.
Я нахожу метод С++ STL, делая простые операции с множеством, довольно неуклюжими в использовании. Например, чтобы найти разницу между двумя наборами:
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;
Поддерживает ли boost альтернативный набор классов, который сводит приведенный выше пример к следующему:
set_type<int> newUserIds = currentUserIds.difference(mPreviousUserIds);
set_type<int> missingUserIds = mPreviousUserIds.difference(currentUserIds);
(Аналогично QSet в Qt, который таким образом переопределяет operator-
.)
См. Алгоритмы настройки набора высоты. Они все еще ожидают выходного итератора.
Неа. Но я вот как его очистить.
Сначала перепишите функции, основанные на итераторе, как функции на основе диапазона. Это уменьшает ваш шаблон.
Во-вторых, им нужно вернуть конструкторы контейнеров, а не принимать итераторы вставки: это дает вам эффективный синтаксис присваивания.
В-третьих, и, возможно, слишком далеко, напишите их как названные операторы.
Конечный результат:
set<int> s = a *intersect* b;
set<int> s2 = c -difference- s;
set<int> s3 = a *_union_* (b *intersect* s -difference- s2);
... после написания лодку кода шаблона в другом месте.
Насколько я знаю, boost делает шаг 1.
Но каждый из этих трех этапов должен значительно уменьшить ваш шаблон.
Конструктор контейнеров:
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_builder
затем может быть неявно передан в любой контейнер, который принимает std::back_inserter
(или имеет свой собственный ADL back_inserter
), а семантика move
на каждом контейнере std
делает конструкцию-то-возвращение довольно эффективной.
Вот моя дюжина строк с именем operator library:
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
. Он поддерживает только один оператор, но его простота проста. Для серьезного использования я бы посоветовал иметь функцию times
, которая по умолчанию вызывает invoke
для *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;
Нет, и я думаю, что у этого никогда не было такого, это общий принцип в С++, который, когда вы можете иметь функцию, не являющуюся членом, чтобы выполнять работу, никогда не делает эту функцию членом. поэтому он не может быть таким, но может Boost:: Range помочь вам.