¿Hay una manera de manejar fácilmente las funciones que devuelven std:: pairs?


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, más tarde 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()));
Author: Jamal, 2016-10-27

6 answers

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 una implementación completa se puede hacer en C++11.

Nota: Mantendría el decltype repetido en el tipo de retorno de deref incluso en C++14, para que SFINAE pueda aplicarse.

Ver en vivo en Coliru

 13
Author: Quentin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:00:21

Con enlace estructurado desde C++1z, puede hacer directamente

auto [lhsMinIt, lhsMaxIt] = std::minmax_element(lhs.begin(), lhs.end());
 32
Author: Jarod42,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-27 11:22:13

Para evitar contaminar su ámbito, puede encerrar la asignación 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, puede usar una 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);
}();
 23
Author: krzaq,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-28 11:57:48

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 lo limitaría a una sola copia de los elementos (que no es un gran problema con ints), también le permitiría 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 al propio contenedor.

 4
Author: Barry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-28 12:11:01

No hay forma de asignar dos referencias a la vez en la revisión actual del estándar, si eso es lo que buscas. Tenga en cuenta que ninguna de las otras respuestas hacen eso, excepto la de Barry que requiere C++17 y una plantilla auxiliar.

Sin embargo, si desea un acceso de lectura-escritura a sus elementos mínimos y máximos, ¿por qué no simplemente ir con los iteradores que el minmax_element le proporciona directamente? Es probable que genere código máquina idéntico al uso de referencias de todos modos, al menos si su lhs es un ContiguousContainer pero tal vez en otros casos también.

Tendrá que confiar un poco menos en la deducción automática de tipos, 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 un poco más limpia decltype(lhs)::iterator.

 2
Author: The Vee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-27 23:36:58

En C++14 o superior

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

Así que fmap_over_tuple toma un objeto de función. Devuelve un objeto de función que, cuando se pasa una tupla-como, procede a llamar al objeto de función en cada elemento de la tupla-como, y generar una tupla a partir de ella.

Luego escribimos tupla de desreferencia:

auto dereference_tuple = fmap_over_tuple(
  [](auto&& e) { return *e; }
);

Ahora en C++17 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. Bastante limpio.

C++14 live example .

 2
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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-28 08:35:06