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".
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.
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
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.
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
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
.
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()
etend()
ou - fonctions libres disponibles
begin()
etend()
(Via ADL)
- fonctions de Membre
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();
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.
À 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.
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é.
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.