Ich arbeite mit viel in C ++ geschriebenem Berechnungscode mit Blick auf hohe Leistung und geringen Speicheraufwand. Es verwendet (meistens vector
) häufig STL-Container und iteriert fast in jeder einzelnen Funktion über diese Container.
Der iterierende Code sieht folgendermaßen aus:
for (int i = 0; i < things.size(); ++i)
{
// ...
}
Es wird jedoch die vorzeichenlose / vorzeichenlose Nichtübereinstimmungswarnung ausgegeben (C4018 in Visual Studio).
Das Ersetzen int
durch einen unsigned
Typ ist ein Problem, da wir häufig OpenMP-Pragmas verwenden und der Zähler erforderlich ist int
.
Ich bin dabei, die (Hunderte von) Warnungen zu unterdrücken, aber ich fürchte, ich habe eine elegante Lösung für das Problem verpasst.
Auf Iteratoren . Ich denke, Iteratoren sind großartig, wenn sie an geeigneten Stellen angewendet werden. Der Code, mit dem ich arbeite, ändert niemals Container mit wahlfreiem Zugriff in list
oder so etwas (das Iterieren mit int i
ist also bereits containerunabhängig) und benötigt immer den aktuellen Index. Und all der zusätzliche Code, den Sie eingeben müssen (Iterator selbst und der Index), macht die Sache nur komplizierter und verschleiert die Einfachheit des zugrunde liegenden Codes.
quelle
int
.int
undstd::vector<T>::size_type
kann auch in der Größe sowie in der Signiertheit unterschiedlich sein. Zum Beispiel auf einem LLP64-System (wie 64-Bit-Windows),sizeof(int) == 4
abersizeof(std::vector<T>::size_type) == 8
.Antworten:
Es ist alles in Ihrem
things.size()
Typ. Es ist nichtint
, abersize_t
(es existiert in C ++, nicht in C), was einem "üblichen" vorzeichenlosen Typ entspricht, dhunsigned int
für x86_32.Der Operator "less" (<) kann nicht auf zwei Operanden mit unterschiedlichem Vorzeichen angewendet werden. Es gibt einfach keine solchen Opcodes und Standard gibt nicht an, ob der Compiler eine implizite Vorzeichenkonvertierung durchführen kann. Es behandelt also nur die signierte Nummer als nicht signiert und gibt diese Warnung aus.
Es wäre richtig, es so zu schreiben
for (size_t i = 0; i < things.size(); ++i) { /**/ }
oder noch schneller
for (size_t i = 0, ilen = things.size(); i < ilen; ++i) { /**/ }
quelle
size_t
. Es iststd::vector< THING >::size_type
.std::size_t
und führen könntestd::vector<T>::size_type
.Idealerweise würde ich stattdessen ein solches Konstrukt verwenden:
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); }
Dies hat den netten Vorteil, dass Ihr Code plötzlich containerunabhängig wird.
Und in Bezug auf Ihr Problem ist die API unordentlich , wenn für eine von Ihnen verwendete Bibliothek eine Verwendung erforderlich ist,
int
bei der eineunsigned int
besser passt. Wenn Sie sicher sind, dass dieseint
immer positiv sind, können Sie Folgendes tun:int int_distance = static_cast<int>(distance);
Dadurch wird Ihre Absicht gegenüber dem Compiler klar angegeben: Sie werden nicht mehr mit Warnungen belästigt.
quelle
static_cast<int>(things.size())
könnten die Lösungen sein, wenn es keine anderen gibt.#pragma warning(push) #pragma warning(disable: 4018) /* ... function */ #pragma warning(pop)
), anstatt eine unnötige Besetzung zu verwenden. (Casts verstecken legitime Fehler, m'kay ?;))Wenn Sie keine Iteratoren verwenden können / wollen und wenn Sie
std::size_t
den Schleifenindex nicht verwenden können / wollen, erstellen Sie eine.size()
to-int
Konvertierungsfunktion, die die Annahme dokumentiert und die Konvertierung explizit ausführt, um die Compiler-Warnung auszuschalten.#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); }
Dann schreiben Sie Ihre Loops wie folgt:
for (int i = 0; i < size_as_int(things); ++i) { ... }
Die Instanziierung dieser Funktionsvorlage wird mit ziemlicher Sicherheit inline sein. In Debug-Builds wird die Annahme überprüft. In Release-Builds ist dies nicht der Fall und der Code ist so schnell, als hätten Sie size () direkt aufgerufen. Keine der beiden Versionen erzeugt eine Compiler-Warnung und es handelt sich nur um eine geringfügige Änderung der idiomatischen Schleife.
Wenn Sie Vermutungsfehler auch in der Release-Version abfangen möchten, können Sie die Behauptung durch eine if-Anweisung ersetzen, die so etwas wie auslöst
std::out_of_range("container size exceeds range of int")
.Beachten Sie, dass dies sowohl den vorzeichenbehafteten / vorzeichenlosen Vergleich als auch das potenzielle
sizeof(int)
! =sizeof(Container::size_type)
Problem löst . Sie können alle Ihre Warnungen aktiviert lassen und sie verwenden, um echte Fehler in anderen Teilen Ihres Codes zu erkennen.quelle
Sie können verwenden:
Zum Beispiel:
// 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()); }
quelle
Ich kann auch folgende Lösung für C ++ 11 vorschlagen.
for (auto p = 0U; p < sys.size(); p++) { }
(C ++ ist nicht klug genug für Auto p = 0, also muss ich p = 0U setzen ....)
quelle
for (auto thing : vector_of_things)
wenn Sie den Index nicht wirklich benötigen.size()
ein Typ zurückgegeben wird, der größer als int ohne Vorzeichen ist, was sehr häufig vorkommt.Ich werde Ihnen eine bessere Idee geben
for(decltype(things.size()) i = 0; i < things.size(); i++){ //... }
decltype
istEs leitet also den Typ von ab
things.size()
undi
wird ein Typ sein, der dem von entsprichtthings.size()
. Soi < things.size()
wird ausgeführt, ohne Vorwarnungquelle
Ich hatte ein ähnliches Problem. Die Verwendung von size_t funktionierte nicht. Ich habe den anderen ausprobiert, der für mich funktioniert hat. (wie nachstehend)
for(int i = things.size()-1;i>=0;i--) { //... }
quelle
Ich würde es einfach tun
int pnSize = primeNumber.size(); for (int i = 0; i < pnSize; i++) cout << primeNumber[i] << ' ';
quelle