c++ - movimiento - ¿El impulso tiene un tipo de datos para operaciones de conjunto que es más simple que el STL?
impulso y cantidad de movimiento ejercicios resueltos (3)
No, y creo que nunca tiene algo así, este es un principio general en C ++ que dice que cuando puedes tener una función no miembro para hacer el trabajo, nunca hagas que esa función sea miembro. entonces no puede ser así, pero puede ser que Boost::Range ayude.
Encuentro que el método C ++ STL de hacer operaciones simples es bastante complicado de usar. Por ejemplo, para encontrar la diferencia entre dos conjuntos:
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;
¿Impulso ofrece un conjunto alternativo de clases que reduciría el ejemplo anterior a algo como esto:
set_type<int> newUserIds = currentUserIds.difference(mPreviousUserIds);
set_type<int> missingUserIds = mPreviousUserIds.difference(currentUserIds);
(Similar a QSet en Qt, que anula al operator-
de esta manera).
Nop. Pero aquí es cómo limpiarlo.
Primero, vuelva a escribir las funciones basadas en iterador como funciones basadas en rangos. Esto reduce a la mitad tu repetición.
Segundo, pídales que devuelvan constructores de contenedores en lugar de tomar iteradores de inserción: esto le brinda una sintaxis de asignación eficiente.
En tercer lugar, y probablemente demasiado lejos, escríbalos como operadores con nombre.
El resultado final es que obtienes:
set<int> s = a *intersect* b;
set<int> s2 = c -difference- s;
set<int> s3 = a *_union_* (b *intersect* s -difference- s2);
... después de escribir un bote de código repetitivo en otro lugar.
Por lo que sé, el impulso hace el paso 1.
Pero cada una de las tres etapas anteriores debería reducir significativamente tu repetición.
Constructor de contenedores:
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_) {}
};
que requiere escribir is_back_insertable
(bastante estándar SFINAE).
Envuelve su functor basado en rangos (o basados en iteradores) que toma un back_insert_iterator como el último argumento, y usa std::bind
para enlazar los parámetros de entrada dejando el último libre. Luego pasa eso a container_builder
, y regresa.
container_builder
puede convertir implícitamente en cualquier contenedor que acepte std::back_inserter
(o tenga su propia ADL back_inserter
), y move
semántica en cada contenedor std
hace que la construcción-luego-retorno sea bastante eficiente.
Aquí está mi docena de línea llamada biblioteca de operador:
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) );
}
}
ejemplo en vivo usándolo para implementar vector *concat* vector
. Solo es compatible con un operador, pero extenderlo es fácil. Para un uso serio, te aconsejo que tengas una función de times
que invoke
de manera predeterminada para *blah*
, un add
para +blah+
que haga lo mismo, etc. <blah>
puede invoke
directamente a invoke
.
Entonces el programador del cliente puede sobrecargar una sobrecarga específica del operador y funciona, o la invoke
general.
Aquí se está utilizando una biblioteca similar para implementar *then*
en ambas funciones y futuros de devolución de tupla.
Aquí hay un primitivo *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;
Ver los algoritmos del conjunto de rango Boost . Sin embargo, todavía esperan un iterador de salida.