Je travaille avec beaucoup de code de calcul écrit en C++ avec pour objectif des performances élevées et une surcharge de mémoire. Il utilise beaucoup les conteneurs STL (la plupart du temps vector
) et effectue une itération sur ceux-ci presque dans chaque fonction.
Le code itératif ressemble à ceci:
for (int i = 0; i < things.size(); ++i)
{
// ...
}
mais il génère l'avertissement incompatibilité signé/non signé (C4018 dans Visual Studio).
Remplacer int
par un type unsigned
est un problème car nous utilisons fréquemment des pragmas OpenMP, et le compteur doit donc être int
.
Je suis sur le point de supprimer les (centaines de) avertissements, mais j'ai bien peur d'avoir manqué une solution élégante au problème.
Sur les itérateurs . Je pense que les itérateurs sont excellents lorsqu'ils sont appliqués aux endroits appropriés. Le code sur lequel je travaille va jamais changer les conteneurs à accès aléatoire en list
ou quelque chose de ce genre (donc l'itération avec int i
est déjà agnostique par le conteneur) et toujours a besoin de l'index actuel. Et tout le code supplémentaire que vous devez taper (l'itérateur lui-même et l'index) ne fait que compliquer les choses et obscurcir la simplicité du code sous-jacent.
Tout est dans votre type things.size()
. Ce n’est pas int
, mais size_t
(il existe en C++, pas en C), ce qui correspond à un type non signé "habituel", c’est-à-dire unsigned int
pour x86_32.
L'opérateur "moins" (<) ne peut pas être appliqué à deux opérandes de signe différent. Il n'existe tout simplement pas de tels opcodes, et la norme ne spécifie pas si le compilateur peut effectuer une conversion de signe implicite. Donc, il traite simplement le numéro signé comme non signé et émet cet avertissement.
Il serait correct de l'écrire comme
for (size_t i = 0; i < things.size(); ++i) { /**/ }
ou même plus vite
for (size_t i = 0, ilen = things.size(); i < ilen; ++i) { /**/ }
Idéalement, j'utiliserais plutôt une construction comme celle-ci:
for (std::vector<your_type>::const_iterator i = things.begin(); i != things.end(); ++i)
{
// if you ever need the distance, you may call std::distance
// it won't cause any overhead because the compiler will likely optimize the call
size_t distance = std::distance(things.begin(), i);
}
Ceci a le net avantage que votre code devient soudainement indépendant du conteneur.
Et en ce qui concerne votre problème, si une bibliothèque que vous utilisez nécessite l’utilisation de int
où un unsigned int
conviendrait mieux, leur API est désordonnée. Quoi qu'il en soit, si vous êtes sûr que ces int
sont toujours positives, vous pouvez simplement faire:
int int_distance = static_cast<int>(distance);
Ce qui précisera clairement votre intention au compilateur: cela ne vous empêchera plus de recevoir des avertissements.
Si vous ne pouvez pas/ne voulez pas utiliser les itérateurs et si vous ne pouvez pas/ne voulez pas utiliser std::size_t
pour l'index de la boucle, créez une fonction de conversion de .size()
à int
qui documente l'hypothèse et effectue la conversion de manière explicite pour désactiver l'avertissement du compilateur.
#include <cassert>
#include <cstddef>
#include <limits>
// When using int loop indexes, use size_as_int(container) instead of
// container.size() in order to document the inherent assumption that the size
// of the container can be represented by an int.
template <typename ContainerType>
/* constexpr */ int size_as_int(const ContainerType &c) {
const auto size = c.size(); // if no auto, use `typename ContainerType::size_type`
assert(size <= static_cast<std::size_t>(std::numeric_limits<int>::max()));
return static_cast<int>(size);
}
Ensuite, vous écrivez vos boucles comme ceci:
for (int i = 0; i < size_as_int(things); ++i) { ... }
L'instanciation de ce modèle de fonction sera presque certainement en ligne. Dans les versions de débogage, l'hypothèse sera vérifiée. Dans les versions release, ce ne sera pas le cas et le code sera aussi rapide que si vous appeliez directement size (). Aucune des versions ne produira un avertissement du compilateur, et il ne s’agit que d’une légère modification de la boucle idiomatique.
Si vous souhaitez également détecter les défaillances d'hypothèses dans la version, vous pouvez remplacer l'assertion par une instruction if qui renvoie quelque chose comme std::out_of_range("container size exceeds range of int")
.
Notez que cela résout à la fois la comparaison signée/non signée ainsi que le problème potentiel sizeof(int)
! = = sizeof(Container::size_type)
. Vous pouvez laisser tous vos avertissements activés et les utiliser pour attraper de vrais bugs dans d'autres parties de votre code.
Vous pouvez utiliser:
Par exemple:
// simple class who output his value
class ConsoleOutput
{
public:
ConsoleOutput(int value):m_value(value) { }
int Value() const { return m_value; }
private:
int m_value;
};
// functional object
class Predicat
{
public:
void operator()(ConsoleOutput const& item)
{
std::cout << item.Value() << std::endl;
}
};
void main()
{
// fill list
std::vector<ConsoleOutput> list;
list.Push_back(ConsoleOutput(1));
list.Push_back(ConsoleOutput(8));
// 1) using size_t
for (size_t i = 0; i < list.size(); ++i)
{
std::cout << list.at(i).Value() << std::endl;
}
// 2) iterators + distance, for std::distance only non const iterators
std::vector<ConsoleOutput>::iterator itDistance = list.begin(), endDistance = list.end();
for ( ; itDistance != endDistance; ++itDistance)
{
// int or size_t
int const position = static_cast<int>(std::distance(list.begin(), itDistance));
std::cout << list.at(position).Value() << std::endl;
}
// 3) iterators
std::vector<ConsoleOutput>::const_iterator it = list.begin(), end = list.end();
for ( ; it != end; ++it)
{
std::cout << (*it).Value() << std::endl;
}
// 4) functional objects
std::for_each(list.begin(), list.end(), Predicat());
}
Je vais vous donner une meilleure idée
for(decltype(things.size()) i = 0; i < things.size(); i++){
//...
}
decltype
est
Inspecte le type déclaré d'une entité ou le type et la catégorie de valeur d'une expression.
Donc, il en déduit que le type de things.size()
et i
sera du même type que things.size()
. Donc, i < things.size()
sera exécuté sans avertissement
Je peux également proposer la solution suivante pour C++ 11.
for (auto p = 0U; p < sys.size(); p++) {
}
(C++ n'est pas assez intelligent pour auto p = 0, je dois donc mettre p = 0U ....)
J'avais un problème similaire. Utiliser size_t ne fonctionnait pas. J'ai essayé l'autre qui a fonctionné pour moi. (comme ci-dessous)
for(int i = things.size()-1;i>=0;i--)
{
//...
}
Je voudrais juste faire
int pnSize = primeNumber.size();
for (int i = 0; i < pnSize; i++)
cout << primeNumber[i] << ' ';