procedimientos - funciones que retornan valores en c++
¿Hay alguna manera de manejar fácilmente las funciones que devuelven std:: pairs? (6)
Con la vinculación estructurada de C ++ 1z, puede hacer directamente
auto [lhsMinIt, lhsMaxIt] = std::minmax_element(lhs.begin(), lhs.end());
C ++ 11 tiene la función std::minmax_element
que devuelve un par de valores. Esto, sin embargo, es bastante confuso de manejar y leer, y produce una variable adicional inútil para contaminar el alcance.
auto lhsMinmax = std::minmax_element(lhs.begin(), lhs.end());
int &lhsMin = *(lhsMinMax.first);
int &lhsMax = *(lhsMinmax.second);
¿Hay una mejor manera de hacer esto? Algo como:
int lhsMin;
int lhsMax;
std::make_pair<int&, int&>(lhsMin, lhsMax).swap(
std::minmax_element(lhs.begin(), lhs.end()));
En C ++ 14 o mayor
template<class=void, std::size_t...Is>
auto indexer( std::index_sequence<Is...> ) {
return [](auto&&f){
return f( std::integral_constant<std::size_t, Is>{}... );
};
}
template<std::size_t N>
auto indexer() {
return indexer( std::make_index_sequence<N>{} );
}
template<class F>
auto fmap_over_tuple( F&& f ) {
return [f=std::forward<F>(f)](auto&& tuple) {
using Tuple = decltype(tuple);
using Tuple_d = std::decay_t<Tuple>;
auto index = indexer< std::tuple_size< Tuple_d >::value >();
return index(
[&f, &tuple](auto&&...Is) {
using std::get;
return std::make_tuple(
f( get<Is>( std::forward<Tuple>(tuple) ) )...
);
}
);
};
}
entonces fmap_over_tuple
toma un objeto de función. Devuelve un objeto de función que, cuando pasa una tupla, procede a llamar al objeto de función en cada elemento de la tupla y genera una tupla a partir de él.
Luego escribimos tupla de desreferencia:
auto dereference_tuple = fmap_over_tuple(
[](auto&& e) { return *e; }
);
Ahora en C ++ 17 lo hacemos:
auto[Min, Max] = dereference_tuple( std::minmax_element(lhs.begin(), lhs.end() );
y Bob es tu tío.
En C ++ 11, solo haz lo que hiciste. Lo suficientemente limpio
Esto parece suficiente de un caso común para solicitar una función auxiliar:
template <class T, std::size_t...Idx>
auto deref_impl(T &&tuple, std::index_sequence<Idx...>) {
return std::tuple<decltype(*std::get<Idx>(std::forward<T>(tuple)))...>(*std::get<Idx>(std::forward<T>(tuple))...);
}
template <class T>
auto deref(T &&tuple)
-> decltype(deref_impl(std::forward<T>(tuple), std::make_index_sequence<std::tuple_size<std::remove_reference_t<T>>::value>{})) {
return deref_impl(std::forward<T>(tuple), std::make_index_sequence<std::tuple_size<std::remove_reference_t<T>>::value>{});
}
// ...
int lhsMin;
int lhsMax;
std::tie(lhsMin,lhsMax) = deref(std::minmax_element(lhs.begin(), lhs.end()));
index_sequence
es C ++ 14, pero se puede realizar una implementación completa en C ++ 11 .
Nota: Mantendré el decltype
repetido en el tipo de devolución de deref
incluso en C ++ 14, para que SFINAE pueda aplicar.
No hay forma de asignar dos referencias a la vez en la revisión actual del estándar, si eso es lo que está buscando. Tenga en cuenta que ninguna de las otras respuestas lo hace, excepto Barry''s que requiere C ++ 17 y una plantilla auxiliar.
Sin embargo, si desea un acceso de lectura y escritura a sus elementos mínimos y máximos, ¿por qué no simplemente ir con los iteradores que minmax_element
proporciona minmax_element
directamente? Es probable que genere códigos de máquina idénticos como el uso de referencias de todos modos, al menos si su lhs
es ContiguousContainer
pero quizás también en otros casos.
Necesitará confiar un poco menos en la deducción de tipo automático, por ejemplo,
decltype(lhs.begin()) lhsMinIt, lhsMaxIt;
std::tie(lhsMinIt, lhsMaxIt) = std::minmax_element(lhs.begin(), lhs.end());
/* now access your minimum and maximum as *lhsMinIt and *lhsMaxIt */
Si sabe que el tipo de lhs
será uno de los contenedores estándar, puede usar una designación de tipo bit cleaner decltype(lhs)::iterator
.
Para evitar contaminar su alcance, puede adjuntar la tarea en un ámbito más pequeño:
int lhsMin, lhsMax;
{
auto it = std::minmax_element(lhs.begin(), lhs.end());
lhsMin = *it.first;
lhsMax = *it.second;
}
alternativamente, puedes usar un lambda
int lhsMin, lhsMax;
std::tie(lhsMin, lhsMax) = [&]{
auto it = std::minmax_element(lhs.begin(), lhs.end());
return std::make_tuple(*it.first, *it.second);
}();
Simplemente sería más directo y escribiría mi propia versión de minmax_element
:
template <class Iter, class R = typename iterator_traits<Iter>::reference>
std::pair<R,R> deref_minmax(Iter first, Iter last)
{
auto iters = std::minmax_element(first, last);
return std::pair<R,R>{*iters.first, *iters.second};
}
Que es entonces solo:
int lo, hi;
std::tie(lo, hi) = deref_minmax(lhs.begin(), lhs.end());
Esto limitaría a una sola copia de los elementos (que no es gran cosa con int
), también le permitirá mantener el acceso a las referencias en el contenedor real.
En C ++ 17, por diversión, podríamos escribir un dereferencer generalizado:
template <class Tuple>
auto deref(Tuple&& tup) {
return std::apply([](auto... args) {
return std::tuple <decltype(*args)...>(*args...);
}, tup);
}
auto& [lo, hi] = deref(std::minmax_element(lhs.begin(), lhs.end()));
Aquí lo
y hi
son referencias en el contenedor en sí.