J'ai une classe de modèle AsIterator
qui prend un type numérique comme dans cet exemple juste un int
, et le convertit en un iterator (++
et --
augmenter et diminuer le nombre et operator*
retourne juste une référence à celui-ci).itérateur inverse retourne les déchets lors de l'optimisation
Cela fonctionne bien à moins qu'il est enveloppé dans un std::reverse_iterator
et compilé avec une optimisation (-O
est suffisant). Lorsque j'optimise le binaire, le compilateur supprime l'appel de dereference au reverse_iterator
et le remplace par une valeur bizarre. Il faut noter qu'il reste fait le bon nombre d'itérations. C'est juste la valeur obtenue par reverse iterator qui est garbage.
Consultez le code suivant:
#include <iterator>
#include <cstdio>
template<typename T>
class AsIterator : public std::iterator<std::bidirectional_iterator_tag, T> {
T v;
public:
AsIterator(const T & init) : v(init) {}
T &operator*() { return v; }
AsIterator &operator++() { ++v; return *this; }
AsIterator operator++(int) { AsIterator copy(*this); ++(*this); return copy; }
AsIterator &operator--() { --v; return *this; }
AsIterator operator--(int) { AsIterator copy(*this); --(*this); return copy; }
bool operator!=(const AsIterator &other) const {return v != other.v;}
bool operator==(const AsIterator &other) const {return v == other.v;}
};
typedef std::reverse_iterator<AsIterator<int>> ReverseIt;
int main() {
int a = 0, b = 0;
printf("Insert two integers: ");
scanf("%d %d", &a, &b);
if (b < a) std::swap(a, b);
AsIterator<int> real_begin(a);
AsIterator<int> real_end(b);
for (ReverseIt rev_it(real_end); rev_it != ReverseIt(real_begin); ++rev_it) {
printf("%d\n", *rev_it);
}
return 0;
}
Cela devrait boucle supposingly à partir du nombre inséré plus haut au plus bas et de les imprimer, comme dans cette course (compilé avec -O0
):
Insert two integers: 1 4
3
2
1
Ce que je reçois avec -O
est à la place:
Insert two integers: 1 4
1
0
0
Vous pouvez try it online here; les nombres peuvent varier mais ils sont toujours "faux" lors de l'optimisation du binaire.
Ce que j'ai essayé:
- hardcoding les entiers d'entrée est suffisant pour produire le même résultat;
- le problème persiste avec gcc 5.4.0 et clang 3.8.0, également lors de l'utilisation libC++;
- faire tous les objets
const
(c'est-à-dire renvoyerconst int &
, et déclarer toutes les variables en tant que telles) ne le fixe pas; En utilisant lereverse_iterator
de la même manière sur par exemple certainsstd::vector<int>
fonctionne très bien; - Si j'utilise simplement
AsIterator<int>
pour une boucle normale avant ou arrière cela fonctionne bien. - dans mes tests, la
0
constante qui est imprimé est en fait hardcoded par le compilateur, les appels àprintf
tous ressembler à ceci lors de la compilation avec-S -O
:
movl $.L.str.2, %edi # .L.str.2 is "%d\n"
xorl %eax, %eax
callq printf
Compte tenu de la cohérence des clang et Le comportement de gcc ici Je suis assez sûr qu'ils le font bien et j'ai mal compris, mais je ne peux vraiment pas le voir.
Fait intéressant, '-fsanitize = undefined' "fixe" il. http://coliru.stacked-crooked.com/a/ddcc7bbf26ab9da2 –
Encore un point de données: g ++ 4.4 imprime '3 2 1' même avec' -O2' tandis que 4.7 affiche '0 0 0' pour moi. Cela crie de comportement indéfini mais je ne le vois pas encore. –
@BaummitAugen Dans le contexte d'origine où ce MVCE est extrait, l'ajout d'une instruction print dans 'operator *' le corrigerait, mais pas dans cet exemple. –