web-dev-qa-db-fra.com

La liaison structurée fonctionne-t-elle avec std :: vector?

Est-il possible d'utiliser une liaison structurée avec des vecteurs?

Par exemple.

std::vector<int> vec{1, 2, 3};
auto [a, b, c] = vec;

Le code ci-dessus ne fonctionne malheureusement pas (sous GCC), mais il existe peut-être une manière différente (avec une liaison structurée) qui permet d'affecter les trois premières valeurs d'un vecteur à trois variables.

23
BartekPL

La liaison structurée ne fonctionne que si la structure est connue au moment de la compilation. Ce n'est pas le cas pour le vector.

Bien que vous connaissiez la structure des éléments individuels, vous ne connaissez pas le nombre d'éléments, et c'est ce que vous essayez de décomposer dans votre question. De même, vous ne pouvez utiliser des liaisons structurées que sur des types de tableau dont la taille est connue au moment de la compilation. Considérer:

void f(std::array<int, 3> arr1,
       int (&arr2)[3],
       int (&arr3)[])
{
    auto [a1,b1,c1] = arr1;
    auto [a2,b2,c2] = arr2;
    auto [a3,b3,c3] = arr3;
}

Les deux premiers fonctionneront, mais la dernière ligne ne pourra pas être compilée, car la taille de arr3 n'est pas connu au moment de la compilation. Essayez-le sur godbolt .

29
ComicSansMS

Il est assez facile de créer un wrapper de base sur votre vecteur qui lui donne accès comme un tuple. Puisqu'il n'y a en effet aucun moyen de récupérer la taille d'un vecteur au moment de la compilation, cela renvoie std::out_of_range si vous essayez de détruire un vecteur trop court. Malheureusement, je ne connais pas de moyen de déduire le nombre de liaisons demandées, c'est donc explicite.

Code complet:

#include <string>
#include <vector>
#include <iostream>

template <class T, std::size_t N>
struct vector_binder {
    std::vector<T> &vec;

    template <std::size_t I>
    T &get() {
        return vec.at(I);
    }
};

namespace std {
    template<class T, std::size_t N>
    struct Tuple_size<vector_binder<T, N>>
    : std::integral_constant<std::size_t, N> { };

    template<std::size_t I, std::size_t N, class T>
    struct Tuple_element<I, vector_binder<T, N>> { using type = T; };
}

template <std::size_t N, class T>
auto dissect(std::vector<T> &vec) {
    return vector_binder<T, N>{vec};
}

int main() {
    std::vector<int> v{1, 2, 3};
    auto [a, b] = dissect<2>(v);

    a = 5;
    std::cout << v[0] << '\n'; // Has changed v through a as expected.
}

Versions Rvalue et const de vector_binder ainsi que de meilleurs noms sont laissés au lecteur comme exercice :)

Voir en direct sur Colir

21
Quentin

Pas idéal car c'est plus verbeux mais vous pouvez aussi faire:

auto [a, b, c] = array<int, 3>({vec[0], vec[1], vec[2]});

Je ne suis pas d'accord avec l'idée que le fait de ne pas connaître le nombre d'éléments d'un conteneur devrait empêcher une liaison structurée à ses éléments. Mon raisonnement est que, puisque ce qui suit ne génère pas d'erreur de temps de compilation:

auto a = vec[0];
auto b = vec[1];
auto c = vec[2];

(même si vec [2] peut être hors de portée au moment de l'exécution), il devrait en être de même pour la liaison structurée ci-dessus. Autrement dit, pourquoi ne pas laisser à l'utilisateur le soin de s'assurer que le vecteur a la bonne longueur au moment de l'exécution, et lever une exception hors plage si ce n'est pas le cas? C'est essentiellement ainsi que nous utilisons les vecteurs partout ailleurs dans la langue.

1
pooya13