web-dev-qa-db-fra.com

Basé sur une plage pour une boucle sur un tableau dynamique?

Il existe une boucle for basée sur la plage avec la syntaxe:

for(auto& i : array)

Il fonctionne avec des tableaux constants mais pas avec des tableaux dynamiques basés sur des pointeurs, comme

int *array = new int[size];
for(auto& i : array)
   cout<< i << endl;

Il donne des erreurs et des avertissements sur l'échec de la substitution, par exemple:

Erreur] C:\Utilisateurs\Siegfred\Documents\C-Libre\Temp\Untitled2.cpp: 16: 16: erreur: pas de fonction correspondante pour l'appel à 'begin (int * &)'

Comment utiliser cette nouvelle syntaxe avec les tableaux dynamiques?

22

Pour utiliser la boucle for basée sur la plage, vous devez fournir des fonctions membres begin() et end() ou surcharger les fonctions non membres begin() et end(). Dans ce dernier cas, vous pouvez envelopper votre gamme dans un std::pair et surcharger begin() et end() pour ceux:

    namespace std {
        template <typename T> T* begin(std::pair<T*, T*> const& p)
        { return p.first; }
        template <typename T> T* end(std::pair<T*, T*> const& p)
        { return p.second; }
    }

Maintenant, vous pouvez utiliser la boucle for comme ceci:

    for (auto&& i : std::make_pair(array, array + size))
        cout << i << endl;

Notez que les fonctions begin() et end() non membres doivent être surchargées dans l'espace de noms std ici, car pair réside également dans l'espace de noms std. Si vous ne souhaitez pas modifier l'espace de noms standard, vous pouvez simplement créer votre propre classe de paires minuscules et surcharger begin() et end() dans votre espace de noms.

Ou bien, créez un wrapper mince autour de votre tableau alloué dynamiquement et fournissez les fonctions membres begin() et end():

    template <typename T>
    struct wrapped_array {
        wrapped_array(T* first, T* last) : begin_ {first}, end_ {last} {}
        wrapped_array(T* first, std::ptrdiff_t size)
            : wrapped_array {first, first + size} {}

        T*  begin() const noexcept { return begin_; }
        T*  end() const noexcept { return end_; }

        T* begin_;
        T* end_;
    };

    template <typename T>
    wrapped_array<T> wrap_array(T* first, std::ptrdiff_t size) noexcept
    { return {first, size}; }

Et votre site d’appel ressemble à ceci:

    for (auto&& i : wrap_array(array, size))
         std::cout << i << std::endl;

Exemple

22
user2218982

Vous ne pouvez pas utiliser plage par boucle avec des tableaux alloués dynamiquement, car le compilateur ne peut pas déduire le début et la fin de ce tableau. Vous devriez toujours utiliser des conteneurs à la place, par exemple std::vector

std::vector<int> v(size);
for(const auto& elem: v)
    // do something
17
awesoon

Vous ne pouvez pas effectuer une boucle basée sur une plage directement sur un tableau alloué dynamiquement car tout ce que vous avez est un pointeur sur le premier élément. Il n'y a aucune information concernant sa taille que le compilateur peut utiliser pour effectuer la boucle. La solution idiomatique C++ consisterait à remplacer le tableau alloué dynamiquement par un std::vector:

std::vector<int> arr(size);
for(const auto& i : arr)
  std::cout<< i << std::endl;

Vous pouvez également utiliser un type de plage qui fournit un itérateur de début et de fin basé sur un pointeur et un décalage. Jetez un coup d’œil à certains types de la bibliothèque boost.range ou de la proposition GSL span (exemple d’implémentation ici , référence du type proposé par C++ 20 ici ).


Notez qu'une plage basée sur la boucle for fonctionne pour les objets std::array dont la taille des tableaux est de taille fixe:

std::array<int,10> arr;
for(const auto& i : arr)
  std::cout<< i << std::endl;

int arr[10] = .... ;
for(const auto& i : arr)
  std::cout<< i << std::endl;

mais dans les deux cas, la taille doit être une constante de compilation.

10
juanchopanza

C++ 20 ajoutera (probablement) std::span , ce qui permet de boucler comme ceci:

#include <iostream>
#include <span>

int main () {
    auto p = new int[5];
    for (auto &v : std::span(p, 5)) {
        v = 1;
    }
    for (auto v : std::span(p, 5)) {
        std::cout << v << '\n';
    }
    delete[] p;
}

Malheureusement, cela ne semble pas encore être supporté par les compilateurs actuels au moment de la rédaction.

Bien sûr, si vous avez le choix, il est préférable d’utiliser dès maintenant le std::vector par rapport aux tableaux de style C. 

1
Baum mit Augen

Au lieu de définir std::begin et std::end pour std::pair de pointeurs (les définir dans std::, soit dit en passant, est comportement non défini ) et déployer votre propre wrapper, comme suggéré avant , vous pouvez utiliser boost::make_iterator_range:

size_t size = 16;
int *dynamic_array = new int[size];
for (const auto& i : boost::make_iterator_range(dynamic_array, dynamic_array + size))
    std::cout << i << std::endl;

Exemple en direct .

0
Dev Null