36 votes

Peut-gamme à base de C++11 pour faire/vérifier des opérations supplémentaires/conditions?

Je suis la découverte de C++11, varient en fonction de la boucle et l'aime déjà. Il vous fait économiser beaucoup de temps lors du codage.

Cependant, je suis habitué à l'écriture de certaines boucles supplémentaires, des déclarations, conditions, et je me demande ce que ceci peut être réalisé lors de l'utilisation de C++11, varient en fonction de la boucle:

1. Extra incrémentation

std::vector<int> v = { 1, 2, 3, 4, 5 };
size_t index = 0;
for ( std::vector<int>::const_iterator iter = v.begin(); iter != v.end(); ++iter, ++index )
{
    std::cout << "v at index " << index << " is " << *iter;
}

Peut devenir:

size_t index = 0;
for ( int val : v )
{
    std::cout << "v at index " << index << " is " << *iter;
    ++index;
}

Cependant, l'incrémentation index dans la for boucle est mieux parce que garanti (incrémenté même si for boucle a continue états par exemple)

Est-il un moyen de déplacer ++index à l'intérieur de l' for déclaration?

2. Obtenez de l'itération de l'index de façon dynamique

std::vector<int> v = { 1, 2, 3, 4, 5 };
for ( std::vector<int>::const_iterator iter = v.begin(); iter != v.end(); ++iter )
{
    std::cout << "v at index " << ( iter - v.begin() ) << " is " << *iter;
}

Quelque chose de similaire réalisé avec C++11 gamme de boucles? Est-il un moyen de savoir combien d'itérations ont été fait jusqu'à présent?

3. Extra condition de sortie

J'utilise souvent dans le code à l'endroit où la rupture est aussi interdit d'utiliser un codage guidline:

std::vector<int> v = { 1, 2, 3, 4, 5 };
bool continueLoop = true;
for ( std::vector<int>::const_iterator iter = v.begin(); iter != v.end() && continueLoop; ++iter )
{
    std::cout << "v value is " << *iter;
    if ( *iter == 4 )
        continueLoop = false;
}

Quelque chose de similaire réalisé avec C++11 gamme de boucles (pause exeuction sans l'aide d'un break)?

14voto

MikeMB Points 5827

Malheureusement, vous ne pouvez pas mettre l'incrément dans la gamme en fonction de boucle. Cependant, dans votre cas particulier - que std::vector stocke ses éléments contigously dans la mémoire, vous pouvez simuler l'option 2 par retomber pointeurs (merci à @M. M et @Jarod42 pour les corrections et améliorations):

for ( const int& val : v )  {
    std::cout << "v at index " << &val-v.data() << " is " << val; 
}

plus générique:

for ( const auto& val : v )  {
    std::cout << "v at index " << std::addressof(val)-v.data() << " is " << val; 
}

L'autre chose que vous pouvez faire est d'écrire un index_range classe, qui représente une des collections d'indices sur lesquels vous pouvez effectuer une itération dans votre gamme de base pour la boucle:

struct index_range_it {
    size_t idx;
    size_t operator*(){
        return idx;
    }
    index_range_it& operator++() {
        idx++;
        return (*this);
    }
};

bool operator!=(index_range_it l,index_range_it r) {
    return l.idx != r.idx;
}

struct index_range {
    size_t size;
    index_range_it end(){return index_range_it{size};}
    index_range_it begin(){return index_range_it{0};}
};

int main()
{
    for (auto i: index_range{v.size()}){
        std::cout << "v at index " << i << " is " << v[i]; 
    }        
}

À part entière de la mise en œuvre de cette idée peut par exemple être trouvés ici

Un tel intervalle peut également être composé de quelque chose, où l'itérateur retourne un objet proxy contenant l'index ainsi qu'une référence à l'objet courant et avec c++17 structurées de liaison qui serait encore plus pratique à utiliser.

13voto

Zulan Points 1216

Jetez un oeil à la gamme-v3 et cppitertools.

cppitertools fournit un moyen très commode enumerate:

std::vector<int> v = { 1, 2, 3, 4, 5 };
for (auto&& e : enumerate(v))
{
    std::cout << "v at index " << e.index << " is " << e.element;
}

Gamme-v3, malheureusement, n'a pas d'énumérer, ce qui me rend très triste, mais vous pouvez composer votre propre en utilisant view::ints et view::zip*. Gamme-v3 qui a le gros avantage, il est la base de la proposition de gammes pour la bibliothèque standard. La gamme de composition permet de construire propre abstractions.

Concernant votre dernier exemple, je dirais que vous devriez éviter une boucle entièrement si vous avez besoin de réduire la complexité. Au lieu d'utiliser un algorithme approprié comme std::find_if, std::any_of qui correspond à votre tâche sans vous avoir à les exprimer de flux de contrôle.

6voto

Matt McNabb Points 14273

Pour un conteneur, vous ne pouvez pas obtenir l'index, ni l'itérateur à partir d'une gamme à base de boucle. Au lieu de cela, vous devez garder une variable distincte, ou revenir à l'itération de la boucle.

L'itérateur regard peut être écrit un peu plus simplement depuis C++11:

for( auto iter = begin(v); iter != end(v); ++iter )

Pour le cas spécifique d'un vecteur que vous pouvez faire:

for ( auto& val : v )
{
    cout << "Index is " << (&val - &v[0]) << '\n';
}

qui fonctionne parce que les vecteurs de l'utilisation de stockage contigu.

4voto

sp2danny Points 1086

Voici un petit quelque chose qui peut faire #2

#include <iterator>
#include <utility>
#include <type_traits>
#include <cstddef>

template<typename Range>
class RangeBasedAdaptor
{
    Range& range;
public:
    RangeBasedAdaptor(Range& r) : range(r) {}
    struct iterator;
    typedef typename std::remove_reference<decltype(*std::begin(range))>::type mapped_type;
    typedef decltype(std::begin(range)) underlying_iterator;

    struct value_type
    {
        std::size_t index() const { return idx; }
        mapped_type& value() { return *ui; }
        const mapped_type& value() const { return *ui; }
    private:
        std::size_t idx;
        underlying_iterator ui;
    friend
        struct iterator;
    };

    struct iterator
    {
        iterator();
        iterator& operator++() { ++val.ui; ++val.idx; return *this; }
        value_type& operator*() { return val; }
        bool operator!=(iterator other) { return val.ui != other.val.ui; }
    private:
        iterator( underlying_iterator ui, std::size_t idx ) { val.idx=idx; val.ui=ui; }
        value_type val;
    friend
        class RangeBasedAdaptor;
    };

    iterator begin() { return iterator{ std::begin(range), 0 }; }
    iterator end() { return iterator{ std::end(range), (std::size_t)-1 }; }
};

template<typename Range>
auto indexed(Range& r) -> RangeBasedAdaptor<Range>
{
    return {r};
}

// -------------------------------------------------------------------------------------

#include <iostream>
#include <vector>
#include <list>

int main()
{
    std::vector<int> foo = { 1,2,3,4,5,6 };

    for( auto& val : indexed(foo) )
    {
        val.value() += 3;
        std::cout << val.index() << " : " << val.value() << std::endl;
    }

    const std::list<float> foo2 = { 1.1f, 2.2f, 3.3f };

    for( auto& val : indexed(foo2) )
    {
        std::cout << val.index() << " : " << val.value() << std::endl;
    }
}

Il est conçu uniquement avec la gamme à base de boucles, d'où le minimum d'itérateur.

3voto

T.E.D. Points 26829

Dans les langages informatiques, traditionnellement une boucle "for" est une boucle avec la langue spécifiée en boucle conditions. Si le programmeur veut spécifier leur propre boucle conditions, ils utilisent une boucle "while". De ce point de vue, C++basés sur la plage pour les boucles sont de la première fois que la langue a jamais vraiment eu un véritable boucle "for" de construire. Ainsi, il peut prendre un programmeur C++ un peu d'envelopper leurs esprits autour du fait que si ils ne peuvent pas traiter avec l'généré par le compilateur boucle conditions, ils doivent être d'utiliser une autre structure.

Cela étant dit, depuis les itérateurs peuvent être des objets personnalisés, vous pouvez faire ce que vous voulez avec une gamme à base de boucle en écrivant vous-même une coutume itérateur. Dans le passé, je ai généralement trouvé cet effort ne vaut pas le code supplémentaire, à moins que vous allez réutiliser itérateur à plusieurs reprises.

1. Extra incrémentation

Cependant, l'incrémentation de l'index dans la boucle for est mieux parce que garanti (incrémenté même si pour la boucle a continuer consolidés pour exemple)

Est-il un moyen de déplacer ++indice à l'intérieur de la déclaration?

Oui, avec un itérateur. Cependant, c'est beaucoup de travail. C'est facile:

for (auto element : container) {
   ++index;
}

Ici, nous savons aussi sa garantie pour obtenir incrémenté, car elle est placée au sommet avant de prendre toute pause ou continuer consolidés.

  1. Obtenez de l'itération de l'index de façon dynamique

Quelque chose de similaire réalisé avec C++11 gamme de boucles? Est il y a un moyen de savoir combien d'itérations ont été fait jusqu'à présent?

Encore une fois, cela pourrait être fait avec un itérateur, mais presque certainement pas la peine. J'ai dû le faire moi-même la semaine dernière, et la solution avait l'air très bien comme le code de la numéro 1 ci-dessus.

  1. Extra condition de sortie

J'utilise souvent dans le code à l'endroit où la rupture est aussi interdit d'utiliser un codage guidline:

Cela devrait jamais être dans un codage de ligne directrice. Son plat de mal. Je suis ne plaide pas pour vous pour briser votre lignes directrices. Mais je le dis pour tous ceux qui lisent ce de ne jamais mettre une telle chose dans un codage d'un document d'orientation à jamais.

Il y a une règle commune de pouce pour bien structuré de codage que tout bloc de code doit avoir un seul point de sortie (aka: goto considéré comme nocif). Cependant, une boucle avec deux états de sortie n'a toujours qu'un seul point de sortie. Les deux sorties de contrôle de retour au même point en dehors de la boucle.

Plus pratiquement, il existe de nombreux types de boucles qui doivent être plus compliqué (par exemple: plus difficile à comprendre et continuer à travailler correctement) si vous ne pouvez pas mettre votre sortie de test au milieu d'eux. Si une directive régulièrement vous oblige à écrire plus obtus code, de son une mauvaise orientation.

Encore une fois, vous pourriez obtenir autour de ce avec un itérateur. Dans ce cas, je dirais qu'il peut être le chemin à parcourir. Bien sûr, ses tonnes de plus que le code de sa valeur juste pour travailler autour de votre stupide de codage de ligne directrice. Mais c'est la ligne directrice de la faute, pas la vôtre.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X