C++ 17: mantener solo algunos miembros al desempaquetar tuple
tuples c++17 (4)
Imaginemos que necesitas llamar al siguiente método:
std::tuple<int, int, int> foo();
En C ++ 17, puede llamar a la función y descomprimir la tupla en una sola línea:
auto [a, b, c] = foo();
Ahora, ¿cómo puedo proceder a almacenar solo
b
y descartar
a
?
Actualmente, solo soy consciente de dos opciones:
1 - Puedo usar una variable ficticia al desempaquetar automáticamente
Sin embargo, la variable ficticia no se usará y emitirá una advertencia, por lo que si quiero silenciar esa advertencia, el código será bastante desagradable de ver:
#pragma warning(push)
#pragma warning(disable:4101)
// ReSharper disable once CppDeclaratorNeverUsed
auto [_, b, c] = foo();
#pragma warning(pop)
2 - Puedo almacenar toda la tupla y usar
std::get
para recuperar la referencia a las únicas variables que necesito.
El código es menos desagradable pero la sintaxis también es menos directa.
Además, el tamaño de este código aumenta en una línea por cada nuevo valor que queremos mantener en la tupla.
auto tuple = foo();
int b = std::get<1>(tuple);
int c = std::get<2>(tuple);
¿Existe otro método más sencillo para descomprimir solo algunos parámetros en una tupla?
Desafortunadamente
, los enlaces estructurados
no admiten explícitamente el descarte de miembros, y los atributos como
[[maybe_unused]]
no pueden aplicarse a
enlaces estructurados
(hay una propuesta para eso:
P0609
: "Atributos para enlaces estructurados"
).
Aquí hay una posible solución:
auto [a, b, c] = foo();
(void) a; // unused
MSVC ya ha fixed esto en VS 15.7 Preview. La versión final de 15.7 debería estar disponible en las próximas semanas. Esto significa que la lógica actual admitida por las últimas versiones de todos los compiladores principales es la siguiente:
- Si se utiliza al menos uno de los enlaces estructurados en una declaración de enlace estructurado, no se emitirá una advertencia de "variable no utilizada" para otros enlaces en la misma declaración.
-
Si no se utiliza ninguno de los enlaces en una declaración de enlace estructurado, es posible silenciar la advertencia utilizando el atributo
[[maybe_unused]]
:[[maybe_unused]] auto [a, b, c] = foo();
Otra alternativa es usar un
std::tie
:
int b, c;
std::tie(std::ignore, b, c) = foo();
Editar
Como se mencionó en los comentarios, hay algunos problemas con este enfoque:
- No hay inferencia de tipo posible
- Los objetos se deben construir antes, por lo que, a menos que los constructores predeterminados sean triviales, no es una buena alternativa.
Podría escribir una función auxiliar que solo le devuelva ciertos índices de un
std::tuple
:
template <size_t... Is, typename Tuple>
auto take_only(Tuple&& tuple) {
using T = std::remove_reference_t<Tuple>;
return std::tuple<std::tuple_element_t<Is, T>...>(
std::get<Is>(std::forward<Tuple>(tuple))...);
}
auto [b, c] = take_only<1, 2>(foo());
O baja la cabeza o algo así:
template <size_t... Is, typename Tuple>
auto drop_head_impl(Tuple&& tuple, std::index_sequence<0, Is...> ) {
return take_only<Is...>(std::forward<Tuple>(tuple));
}
template <typename Tuple>
auto drop_head(Tuple&& tuple) {
return drop_head_impl(std::forward<Tuple>(tuple),
std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>());
}
auto [b, c] = drop_head(foo());
Pero las implementaciones anteriores seguramente tienen algunos problemas de complejidad de por vida que el uso directo de enlaces estructurados no lo hará, ya que no hay ninguna extensión de por vida aquí.
Así que, haz lo que dice Vittorio :
auto [a, b, c] = foo();
(void)a;