gpt4 book ai didi

c++ - 编译器优化打破了惰性迭代器

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:43:27 26 4
gpt4 key购买 nike

我用自定义迭代器编写了一个自定义容器。由于容器的特殊特性,必须延迟计算迭代器。为了这个问题,代码的相关部分是以这种方式实现的迭代器的取消引用运算符

template<typename T>
struct Container
{
vector<T> m_Inner;

// This should calculate the appropriate value.
// In this example is taken from a vec but in
//the real use-case is calculated on request
T Value(int N)
{ m_Inner.at(N); }
}

template<typename T>
struct Lazy_Iterator
{
mutable pair<int, T> m_Current;
int Index
Container<T>* C

Lazy_Iterator(const Container& Cont, int N):
m_Current{Index, T{}}, Index{N}, C{&Cont}
{ }

pair<int, T>&
operator*() const // __attribute__((noinline)) (this cures the symptom)
{
m_Current.first = Index; /// Optimized out
m_Current.second = C->Value(Index); /// Optimized out
return m_Current;
}

}

因为迭代器本身就是一个模板,它的函数可以被编译器自由内联。

当我在没有优化的情况下编译代码时,返回值会按预期更新。在某些情况下,当我使用发布编译器优化(GCC 4.9 中的 -O2)时,编译器优化了我标记为 Optimized out 的行,即使 m_Current 成员被标记为可变。因此,返回值与迭代器应指向的值不匹配。

这是预期的行为吗?您是否知道任何可移植的方法来指定该函数的内容即使被标记为 const 也应该被评估?

我希望这个问题足够详尽以便有用。如果更多详细信息对这种情况有帮助,请提出建议。

编辑:

回答一个评论,这是从一个小测试程序中提取的潜在用法:

Container<double> myC;
Lazy_Iterator<double> It{myC, 0}
cout << "Creation: " << it->first << " , " << it->second << endl;

auto it2 = it;
cout << "Copy: "<< it2->first << " , " << it2->second << endl;

cout << "Pre-increment: " << (it++)->first << " , " << it->second << endl;
cout << "Post-increment: " << (++it)->first << " , " << it->second << endl;
cout << "Pre-decrement: " << (it--)->first << " , " << it->second << endl;
cout << "Post-decrement: " << (--it)->first << " , " << it->second << endl;
cout << "Iterator addition: " << (it+2)->first << " , " << (it+2)->second << endl;
cout << "Iterator subtraction: "<< (it-2)->first << " , " << (it-2)->second << endl;

reverse_iterator<Lazy_Iterator> rit{it};
cout << "Reverse Iterator: " << rit->first << " , " << rit->second << endl;

auto rit2 = rit;
cout << "Reverse Iterator copy: " << rit2->first << " , " << rit2->second << endl;

cout << "Rev Pre-increment: " << (rit++)->first << " , " << rit->second << endl;
cout << "Rev Post-increment: " << (++rit)->first << " , " << rit->second << endl;
cout << "Rev Pre-decrement: " << (rit--)->first << " , " << rit->second << endl;
cout << "Rev Post-decrement: " << (--rit)->first << " , " << rit->second << endl;
cout << "Rev Iterator addition: " << (rit+2)->first << " , " << (rit+2)->second << endl;
cout << "Rev Iterator subtraction: "<< (rit-2)->first << " , " << (rit-2)->second << endl;

除最后两行外,所有测试的测试结果都符合预期

测试的最后两行在打开优化时中断。

该系统实际上运行良好,并不比任何其他迭代器更危险。当然,如果容器在他眼皮子底下被删除,它会失败,通过复制使用返回值可能更安全,而不仅仅是保留引用,但这是题外话

最佳答案

reverse_iterator 持有的物理迭代器(.base() 返回的内容)和它指向的逻辑值之间存在差异:它们是关闭的-一个。 reverse_iterator might do return *(--internal_iterator); on dereference ,这给您留下了对已销毁的局部函数临时内部结构的悬空引用。

再次阅读标准后,我发现它有额外的要求来避免这种情况,请阅读注释。

我还发现 GCC 4.9 标准库不兼容。它使用一个临时的。所以,我认为这是一个 GCC 错误。

编辑:标准报价

24.5.1.3.4 operator*     [reverse.iter.op.star]

reference operator*() const;

1 Effects:

deref_tmp = current;  
--deref_tmp;
return *deref_tmp;

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 ]

后续阅读: Library Defect Report 198 .

it seems它是returned to old behaviour .

后期编辑:P0031 在 C++17 工作草案中投票。它指出 reverse_iterator 使用临时的,而不是成员来保存中间值。

关于c++ - 编译器优化打破了惰性迭代器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35770904/

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