Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/146.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 编译器优化破坏了惰性迭代器_C++_Optimization_Iterator_Lazy Evaluation_Gcc4.9 - Fatal编程技术网

C++ 编译器优化破坏了惰性迭代器

C++ 编译器优化破坏了惰性迭代器,c++,optimization,iterator,lazy-evaluation,gcc4.9,C++,Optimization,Iterator,Lazy Evaluation,Gcc4.9,我用自定义迭代器编写了一个自定义容器。由于容器的特定特性,迭代器必须延迟计算。为了解决这个问题,代码的相关部分是迭代器的解引用操作符,它是以这种方式实现的 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

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

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

}
模板
结构容器
{
向量m_-内;
//这应该计算出适当的值。
//在本例中,取自vec,但在
//实际用例是根据请求计算的
T值(整数N)
{m_Inner.at(N);}
}
模板
结构惰性迭代器
{
可变对m_电流;
整数索引
集装箱*C
惰性迭代器(常量容器&Cont,int N):
m_当前{Index,T{},Index{N},C{&Cont}
{      }
配对&
运算符*()常量//(noinline))(这可以治愈症状)
{
m_Current.first=Index;///优化输出
m_Current.second=C->Value(Index);///优化输出
返回m_电流;
}
}
因为迭代器本身是一个模板,它的函数可以由编译器自由内联

当我编译没有优化的代码时,返回的值会按预期更新。在某些情况下,当我使用发行版编译器优化(-GCC4.9中的O2)时,编译器会优化掉我标记为优化掉的行,即使m_当前成员标记为可变的。因此,返回值与迭代器应该指向的值不匹配

这是预期的行为吗?您是否知道任何可移植的方法来指定即使该函数标记为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;
集装箱myC;
惰性迭代器It{myC,0}

cout如果您必须发布一个可编译的代码段来重现该问题(实际上我无法用GCC4.9重现它),我认为您有未定义的行为,这是由O2触发的(O2支持可以破坏未定义行为的优化)。你应该有一个指向

Container<T> 
容器
在迭代器内部

无论如何,请注意,惰性迭代器破坏了std迭代器的契约,我认为更好的选择是创建一个包含惰性值的常规容器,您可以通过这种方式跳过以创建一个自定义容器和迭代器;)(查看代理模式)。

“即使当前
m_
成员标记为可变,也已优化”

这告诉我,您假设优化器关心的是
mutable
。没有<代码>常量
可变
已被早期编译阶段剥离

如果这两条语句是内联的,那么为什么Optimier会删除它们呢?我怀疑在内联之后,优化器可以证明这两个写操作是不可操作的,因为
m_Current
变量必须已经包含正确的值,或者因为
m_Current
的后续使用使得它没有意义。以下情况使这些写入成为不可操作:

Lazy_Iterator LI = foo(); // Theoretically writes
*LI = bar(); // Overwrites the previous value.

reverse\u iterator
(由
.base()
返回的内容)持有的物理迭代器与它指向的逻辑值之间的差异存在问题:它们相差1,这会让您悬而未决地引用被破坏的函数local-temporary的内部

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

我还发现GCC4.9标准库不符合要求。它使用一个临时的。所以,我认为这是一个GCC错误

编辑:标准报价

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

reference operator*() const;
1影响:

deref_tmp = current;  
--deref_tmp; 
return *deref_tmp;
2[注意:此操作必须使用辅助成员变量而不是临时变量,以避免返回在其相关迭代器的生存期之后仍然存在的引用。(请参见24.2.)-结束注意]

后续阅读:

事实就是这样


后期编辑:P0031在C++17工作草案中投票。它声明
反向迭代器
使用临时而非成员来保存中间值

经过一轮非常有益的讨论,Revolver\u Ocelot的答案让我进一步研究反向迭代器的实现。根据他引用的标准:

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

参考运算符*()常量

1影响:

deref_tmp = current;  
--deref_tmp;  
return *deref_tmp;
2[注意:此操作必须使用辅助成员变量而不是临时变量,以避免 返回一个在引用的生存期之后仍然存在的引用 相关迭代器(见24.2.)-结束注释]

查看Debian 8中GCC 4.9实现的标准库的头stl_iterator.c的内部:

  /**
   *  @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
  */
  reference
  operator*() const
  {
_Iterator __tmp = current;
return *--__tmp;
  }
请注意警告:

警告: 此实现要求 底层迭代器类型@cx,由 @在@c x被修改或修改后,c*x仍然有效 摧毁。这是一个bug:


这是一个很好的问题,但是你认为你可以编辑代码片段来找出拼写错误吗?你指的是哪种拼写错误?我更改了那些我忘记更换的TypeDef的类型。如果还有更多,请告诉我您能提供一份吗?当前问题中的代码看起来正确。由于返回引用看起来可疑,您如何使用此迭代器?我认为这个问题可以在其他地方找到。可能:可能会有帮助。我确实忘记了模板参数。我纠正了它。我还在测试代码段中添加了更多行。如果启用优化,则失败的测试是最后两个,即反向迭代器上的算法。在这种情况下,将在内部生成前向迭代器的副本