C++11 basé sur la plage pour les boucles sans variable de boucle

En C++ j'ai besoin de réitérer un certain nombre de fois, mais je n'ai pas besoin d'une variable d'itération. Par exemple:

for( int x=0; x<10; ++x ) {
    /* code goes here, i do not reference "x" in this code */
}

Je me rends compte que je peux le faire en remplaçant "code goes here" par un lambda ou une fonction nommée,mais cette question concerne spécifiquement les boucles.

J'espérais que les boucles For basées sur la plage de C++11 aideraient:

for( auto x : boost::irange(0,10) ) {
    /* code goes here, i do not reference "x" in this code */
}

Mais ce qui précède donne une "variable locale non référencée" puisque je ne fais jamais explicitement référence à X.

Je me demande s'il y a un plus manière élégante d'écrire ce qui précède pour les boucles afin que le code ne génère pas d'avertissement "variable locale non référencée".

50
demandé sur nonagon 2013-07-18 02:52:54

9 réponses

Il peut y avoir un moyen de le faire mais jetrès doute beaucoup que ce serait plus élégant. Ce que vous avez dans cette première boucle est déjà la bonne façon de le faire, limitant la portée/durée de vie de la variable de boucle.

Je voudrais simplement ignorer l'avertissement de variable inutilisé (c'est seulement une indication du compilateur que quelque chose Peut être faux, après tout) ou utiliser les installations du compilateur (si disponible) pour simplement désactiver l'avertissement à ce moment-là.

Cela peut être possible avec une sorte de #pragma en fonction de votre environnement, ou certaines implémentations vous permettent de faire des choses comme:

for (int x = 0; x < 10; ++x) {
    (void)x;

    // Other code goes here, that does not reference "x".
}

J'ai vu cette astuce void utilisée pour les paramètres inutilisés dans les corps de fonction.

18
répondu paxdiablo 2017-12-14 12:44:12

Edit {[6] } Maintenant avec 100% moins de variables de boucle déclarées.

template <typename F>
void repeat(unsigned n, F f) {
    while (n--) f();
}

Utilisez-le comme:

repeat(10, f);

Ou

repeat(10, [] { f(); });

Ou

int g(int);
repeat(10, std::bind(g, 42));

Voir vivre à http://ideone.com/4k83TJ

39
répondu sehe 2013-07-19 08:09:28

En supposant que 10 est une constante de compilation...

#include <cstddef>
#include <utility>
template<std::size_t N>
struct do_N_times_type {
  template<typename Lambda>
  void operator()( Lambda&& closure ) const {
    closure();
    do_N_times_type<N-1>()(std::forward<Lambda>(closure));
  }
};
template<>
struct do_N_times_type<1> {
  template<typename Lambda>
  void operator()( Lambda&& closure ) const {
    std::forward<Lambda>(closure)();
  }
};
template<>
struct do_N_times_type<0> {
  template<typename Lambda>
  void operator()( Lambda&& closure ) const {
  }
};

template<std::size_t N, typename Lambda>
void do_N_times( Lambda&& closure ) {
  do_N_times_type<N>()( std::forward<Lambda>(closure) );
};
#include <iostream>
void f() {
  std::cout << "did it!\n";
}
int main() {
  do_N_times<10>([&]{
    f();
  });
}

Ou juste

int main() {
  do_N_times<10>(f);
}

Autres méthodes ridicules:

Écrire une série d'itérateur (j'ai appeler le mien index) qui produit une gamme de itérateur sur les types intégraux (j'ai par défaut std::size_t). Puis tapez:

for( auto _:index_range(10) )

, Qui utilise une variable (_), mais semble extrêmement déroutant.

Une autre approche folle serait de créer un générateur de type python. Écrire un wrapper de générateur qui prend une plage itérable et produit une fonction qui renvoie std::optional sur le value_type de la plage n'est pas difficile.

Nous pouvons alors faire:

auto _ = make_generator( index_range(10) );
while(_()) {
}

Qui crée également une variable temporaire, et est encore plus obtus.

Nous pourrions écrire une fonction de boucle qui fonctionne sur des générateurs:

template<typename Generator, typename Lambda>
void While( Generator&& g, Lambda&& l ) {
  while(true) {
    auto opt = g();
    if (!opt) return;
    l(*opt);
  }
}

Que nous appelons alors comme:

While( make_generator( index_range(10) ), [&](auto&&){
  f();
});

Mais cela crée à la fois des variables temporaires dans la fonction, et est plus ridicule que la dernière, et repose sur des fonctionnalités de C++1y qui n'a même pas été finaliser.

Ceux où mes tentatives de créer un moyen sans variable de répéter quelque chose 10 fois.

Mais vraiment, je ferais juste la boucle.

, Vous pouvez presque certainement bloquer l'avertissement en tapant x=x;

Ou écrire une fonction

template<typename Unused>
void unused( Unused&& ) {}

Et call unused(x); -- la variable x est utilisée, et son nom est déposé à l'intérieur, donc le compilateur peut ne pas vous en avertir à l'intérieur.

Alors essayez ceci:

template<typename Unused>
void unused( Unused&& ) {}
for(int x{};x<10;++x) {
  unused(x);
  f();
}

, Qui devrait supprimer l'avertissement, et être en fait facile à comprendre.

9
répondu Yakk - Adam Nevraumont 2013-07-17 23:31:24

Il y a effectivement un moyen de faire ce travail. Tout ce que vous devez faire est de retourner un std::array avec la longueur spécifiée par la constante que vous fournissez:

template <int N>
using range = std::array<int, N>;

int main()
{
    for (auto x : range<5>())
    {
        std::cout << "Awesome\n";
    }
}

Sortie:

Génial
Génial
Génial
Génial
Génial

Voici une démo.

Remarque: C'est en supposant que la gamme spécificateur est une constante de compilation, donc, si vous devez utiliser une variable assurez-vous qu'il est valablement marqué constexpr.

7
répondu 0x499602D2 2013-07-18 23:54:47

Il n'y a aucun moyen de faire une plage basée sur le travail pour simplement itérer sur plusieurs nombres.

C++11 Les boucles basées sur des plages nécessitent une expression à distance qui peut être:

  • un tableau ou
  • une classe ayant
    • fonctions de Membre begin() et end() ou
    • fonctions libres disponibles begin() et end() (Via ADL)

En plus de cela: une plage basée sur For produit des frais généraux:

for ( for_range_declaration : expression ) statement

Se développe à

range_init = (expression)
{
  auto && __range = range_init;
  for ( auto __begin = begin_expr,
  __end = end_expr;
  __begin != __end;
  ++__begin ) {
    for_range_declaration = *__begin;
    statement;
  }
}

Où begin_expr et end_expr sont obtenus via l'inspection de tableau ou begin() / end() paires.

Je ne pense pas que cela soit "plus propre" qu'un simple for-loop. Surtout à l'égard de la performance. Pas d'appels, juste une boucle simple.

La seule façon de le rendre plus élégant (où elegant est clairement soumis à mon opinion) est d'utiliser une taille ou un type non signé ici:

for(size_t x(0U); x<10U; ++x) f();
4
répondu Pixelchemist 2013-07-17 23:29:28

Déjà mieux répondu dans https://stackoverflow.com/a/21800058/1147505 : Comment définir une macro inutilisée à utiliser dans votre base de code, ce qui supprime cet avertissement. D'une manière portable.

4
répondu user1147505 2017-05-23 11:46:48

À mon avis, vous abusez de la boucle basée sur la plage. La boucle basée sur la plage doit être utilisée lorsque la logique est: " pour chaque élément de la collection, faites quelque chose ". L'idée est de se débarrasser de la variable d'index car il n'est pas important. Si vous avez une collection, vous devez l'Instrumenter avec les API nécessaires pour activer l'itération basée sur la plage. Si vous n'avez pas de collection, vous n'avez aucune affaire à utiliser une boucle basée sur la plage (en fait, c'est ce que le compilateur implique dans not-so-informative façon). Dans cette situation, une boucle for/while normale est le choix naturel.

3
répondu SomeWittyUsername 2013-07-18 18:09:02

Vous pouvez utiliser le STL avec une expression lambda.

#include <algorithm>
#include <iostream>

int main() {
    int a[] = {1,2,3,4,5,6};

    std::for_each(std::begin(a),
            std::end(a),
            [](int){std::cout << "Don't care" << std::endl;});
}

Cette approche fonctionne également pour des conteneurs arbitraires tels que des vecteurs ou des listes. Laissez vector<int> a, alors vous appelleriez a.begin() et a.end(). Notez que vous pouvez également utiliser un pointeur de fonction au lieu d'une expression lambda.

Ce qui précède préserve votre notion d'utilisation d'un foreach, tout en ne se plaignant pas d'un paramètre inutilisé.

3
répondu evnu 2013-07-18 18:39:41

Cela fonctionne dans GCC et clang et tout compilateur qui prend en charge les attributs gnu:

for( [[gnu::unused]] auto x : boost::irange(0,10) ) {

Et devrait compiler dans n'importe quel compilateur C++11 mais ne peut pas supprimer l'avertissement si le compilateur ne reconnaît pas les attributs gnu.

1
répondu atb 2015-10-16 18:10:20