gpt4 book ai didi

c++ - 反向迭代器在优化时返回垃圾

转载 作者:IT老高 更新时间:2023-10-28 14:01:20 24 4
gpt4 key购买 nike

我有一个 AsIterator采用类数字类型的模板类,在此示例中只是 int , 并将其转换为迭代器(++-- 递增和递减数字,operator* 只是返回对它的引用)。

这很好用除非它被包装到 std::reverse_iterator 中并使用任何优化编译(-O 就足够了)。当我优化二进制文件时,编译器会去掉对 reverse_iterator 的取消引用调用。并用一些奇怪的值替换它。必须注意的是,它仍然进行正确的迭代次数。只是反向迭代器得到的值是垃圾。

考虑以下代码:

#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;
}

这应该从插入的最高数字向下循环到最低并打印它们,例如在此运行中(使用 -O0 编译):

Insert two integers: 1 4 
3
2
1

我通过 -O 得到的结果而是:

Insert two integers: 1 4 
1
0
0

您可以try it online here ;数字可能会有所不同,但在优化二进制文件时它们总是“错误”。


我尝试过的:

  • 硬编码输入整数足以产生相同的结果;
  • 使用 gcc 5.4.0clang 3.8.0 时问题仍然存在,使用 libc++ 时也是如此;
  • 制作所有元素const (即返回 const int & 并声明所有变量)并不能解决此问题;
  • 使用 reverse_iterator以同样的方式,例如一些 std::vector<int>工作正常;
  • 如果我只使用 AsIterator<int>对于正常的前向或后向循环,它可以正常工作。
  • 在我的测试中,常数 0打印出来的内容实际上是由编译器硬编码,调用printf使用 -S -O 编译时看起来像这样:
    movl    $.L.str.2, %edi  # .L.str.2 is "%d\n"
xorl %eax, %eax
callq printf

鉴于 clanggcc 在这里的行为的一致性,我很确定他们做得对,但我误解了,但我真的看不到.

最佳答案

查看 std::reverse_iteratorlibstdc++ 实现会发现一些有趣的事情:

  /**
* @return A reference to the value at @c --current
*
* This requires that @c --current is dereferenceable.
*
* @warning This implementation requires that for an iterator of the
* underlying iterator type, @c x, a reference obtained by
* @c *x remains valid after @c x has been modified or
* destroyed. This is a bug: http://gcc.gnu.org/PR51823
*/
_GLIBCXX17_CONSTEXPR reference
operator*() const
{
_Iterator __tmp = current;
return *--__tmp;
}

@warning 部分告诉我们,对底层迭代器类型的要求是,即使在底层迭代器被修改/销毁后,*x 也必须保持有效。

mentioned bug link揭示更多有趣的信息:

at some point between C++03 and C++11 the definition of reverse_iterator::operator* was changed to clarify this, making libstdc++'s implementation wrong. The standard now says:

[ Note: This operation must use an auxiliary member variable rather than a temporary variable to avoid returning a reference that persists beyond the lifetime of its associated iterator. (See 24.2.) —end note ]

Jonathan Wakely 的评论(2012 年)

所以它看起来像一个错误......但在主题的结尾:

The definition of reverse_iterator has been reverted to the C++03 version, which does not use an extra member, so "stashing iterators" can not be used with reverse_iterator.

Jonathan Wakely 的评论(2014 年)

因此,似乎将 std::reverse_iterator 与“存储迭代器”一起使用确实会导致 UB。


DR 2204: "reverse_iterator should not require a second copy of the base iterator"进一步澄清问题:

This note in 24.5.1.3.4 [reverse.iter.op.star]/2:

[ Note: This operation must use an auxiliary member variable rather than a temporary variable to avoid returning a reference that persists beyond the lifetime of its associated iterator. (See 24.2.) —end note ]

[my note: I think that the above note would fix your UB issue]

是不正确的,因为这样的迭代器实现被 24.2.5 [forward.iterators]/6 排除在外,它说:

If a and b are both dereferenceable, then a == b if and only if *a and *b are bound to the same object.

关于c++ - 反向迭代器在优化时返回垃圾,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41898473/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com