c++ - ¿Hay una buena manera de asignar std:: minmax(a, b) a std:: tie(a, b)?
algorithm reference (3)
A veces, dar un paso atrás y encontrar una manera diferente da sus frutos:
if (b < a)
std::iter_swap(&a, &b);
Eso es conciso y generalmente más eficiente, ciertamente al menos a la par. Tal vez empaquetarlo en su propia función:
template <class T>
void reorder(T& a, T& b)
noexcept(noexcept(b < a, void(), std::iter_swap(&a, &b))) {
if (b < a)
std::iter_swap(&a, &b);
}
Estoy usando
std::iter_swap()
así que no tengo que usar el
using std::swap; swap(a, b)
using std::swap; swap(a, b)
dos pasos para generalidad en pre-C ++ 2a, que introduce
objetos de punto de personalización
que lo hacen obsoleto.
std::tie(a, b) = std::minmax(a, b);
Creo que este es un código intuitivo.
Limpio y comprensible.
Lástima que no funcione según lo previsto, como las plantillas
std::minmax
para
const&
.
Por lo tanto, si los valores se intercambian dentro del
std::pair<const&, const&>
una asignación sobrescribirá el otro valor:
auto[a, b] = std::make_pair(7, 5);
std::tie(a, b) = std::minmax(a, b);
std::cout << "a: " << a << ", b: " << b << ''/n'';
a: 5, b: 5
La salida esperada aquí es
a: 5, b: 7
.
Creo que esto es importante ya que la implementación de funciones de transformación para aplicar una función en algunos rangos requiere tales declaraciones para lambdas intuitivas. Por ejemplo:
std::vector<int> v{ 0, 1, 0, 2, 0 };
std::vector<int> u{ 1, 0, 1, 0, 1 };
perform(v.begin(), v.end(), u.begin(), [](auto& a, auto& b){
std::tie(a, b) = std::minmax(a, b);
});
//v would be == {0, 0, 0, 0, 0}
//u would be == {1, 1, 1, 2, 1}
Una solución que encontré fue la construcción de un
std::tuple
explícitamente sin ningún calificador de referencia sobre el
std::pair<const&, const&>
para imponer una copia:
std::tie(a, b) = std::tuple<int, int>(std::minmax(a, b));
Pero esta redundancia
<int, int>
parece bastante horrible, especialmente cuando se dice antes
auto& a, auto& b
.
¿Hay una forma bonita y corta de realizar esta tarea?
¿Podría ser que esta es la dirección equivocada y solo decir
if (a >= b) { std::swap(a, b); }
¿Sería el mejor enfoque aquí?
Puede aplicar esto con un cierto nivel de brevedad de la siguiente manera.
std::tie(a, b) = std::minmax(+a, +b);
std::cout << "a: " << a << ", b: " << b << ''/n'';
Explicación: el operador integrado unary plus, en aras de la simetría con su hermano menos unario, devuelve su operando
por valor
(también realiza las conversiones aritméticas habituales, pero eso no se aplica a
int
s).
Esto significa que tiene que crear un temporal, aunque este temporal no es más que una copia del operando.
Pero para el uso de
minmax
en este ejemplo, es suficiente: intercambiar referencias aquí ya no se asigna, porque las referencias en el lado derecho (la
const int&
argumentos pasados a
minmax
) no se refieren a los mismos objetos que aquellos en el lado izquierdo (dentro de la
tuple
de referencias creadas por
std::tie
).
La salida es la deseada:
a: 5, b: 7
Puedes usar una lista de inicialización para
minmax
:
std::tie(a, b) = std::minmax({a, b});
Esto hace que se creen objetos temporales, al igual que cuando se usa unary plus , pero tiene la ventaja de que funciona con los tipos que carecen del operador unary plus .
using namespace std::string_view_literals;
auto [a, b] = std::make_pair("foo"sv, "bar"sv);
std::tie(a, b) = std::minmax({a, b});
std::cout << "a: " << a << ", b: " << b << ''/n'';
Salida:
a: bar, b: foo
¿Podría ser que esta es la dirección equivocada y solo decir
if (a >= b) { std::swap(a, b); }
¿Sería el mejor enfoque aquí?
Lo haría
if(b < a) std::swap(a, b);
debido al requisito de la
Compare
1
, pero sí, sospecho que será más rápido y aún es muy claro lo que quiere lograr.
[1] Comparar [...] El valor de retorno de la operación de llamada de función aplicada a un objeto de un tipo que satisface la Comparación, cuando se convierte contextualmente a bool, produce verdadero si el primer argumento de la llamada aparece antes del segundo en el punto débil estricto Relación de orden inducida por este tipo, y en caso contrario falsa.