web-dev-qa-db-fra.com

C ++ 17: ne conserve que quelques membres lors du décompactage de Tuple

Imaginons que vous deviez appeler la méthode suivante:

std::Tuple<int, int, int> foo();

En C++ 17, vous pouvez appeler la fonction et décompresser le tuple sur une seule ligne:

auto [a, b, c] = foo();

Maintenant, comment puis-je continuer à stocker uniquement b et c et à ignorer a?

Actuellement, je ne connais que deux options:


1 - Je peux utiliser une variable factice lors de la décompression automatique

Cependant, la variable muette sera inutilisée et émettra un avertissement. Par conséquent, si je veux taire cet avertissement, le code sera assez désagréable à voir:

#pragma warning(Push)
#pragma warning(disable:4101)
// ReSharper disable once CppDeclaratorNeverUsed
auto [_, b, c] = foo();
#pragma warning(pop)

2 - Je peux stocker le tuple entier et utiliser std::get pour récupérer la référence aux seules variables dont j'ai besoin. Le code est moins désagréable mais la syntaxe est également moins simple.

De plus, la taille de ce code augmente d'une ligne pour chaque nouvelle valeur que nous souhaitons conserver dans le tuple.

auto Tuple = foo();
int b = std::get<1>(Tuple);
int c = std::get<2>(Tuple);

Existe-t-il une autre méthode plus simple pour décompresser uniquement quelques paramètres dans un tuple?

63
Antoine C.

Une autre alternative consiste à utiliser un std::tie:

int b, c;
std::tie(std::ignore, b, c) = foo();

Éditer

Comme mentionné dans les commentaires, cette approche pose certains problèmes:

  • Aucune inférence de type possible
  • Les objets doivent être construits avant. Par conséquent, à moins que les constructeurs par défaut soient triviaux, ce n'est pas une bonne alternative.
42
Mansuro

Malheureusement, liaisons structurées == ne prend pas explicitement en charge le rejet de membres et d'attributs tels que [[maybe_unused]] ne peut pas être appliqué à liaisons structurées (il existe une proposition pour cela: P0609 : "Attributs pour les liaisons structurées" ).

Voici une solution possible:

auto [a, b, c] = foo();
(void) a; // unused
39
Vittorio Romeo

Vous pouvez écrire une fonction d'assistance qui ne vous restitue que certains indices d'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());

Ou laisse tomber la tête ou quelque chose:

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());

Mais les implémentations ci-dessus ont presque certainement des problèmes de complexité de la durée de vie que l'utilisation directe de liaisons structurées ne résoudra pas - car il n'y a pas d'extension de durée de vie ici.

Alors, faites quoi dit Vittorio :

auto [a, b, c] = foo();
(void)a;
21
Barry

MSVC a déjà corrigé ceci dans l'aperçu VS 15.7. La version finale 15.7 devrait être disponible dans les prochaines semaines. Cela signifie que la logique actuelle prise en charge par les dernières versions de tous les principaux compilateurs est la suivante:

  • Si au moins une des liaisons structurées d'une déclaration de liaison structurée est utilisée, aucun avertissement "Variable non utilisée" ne sera émis pour les autres liaisons de la même déclaration.
  • Si aucune des liaisons dans une déclaration de liaison structurée n'est utilisée, il est possible de faire taire l'avertissement à l'aide de la commande [[maybe_unused]] attribut:

    [[peut-être_utilisé]] auto [a, b, c] = foo ();
5
Igor Akhmetov