C ++ STL优化警告:代码问题还是更险恶的?

时间:2011-01-29 03:08:05

标签: c++ optimization gcc stl vector

我有一个程序,我正在处理从使用数组切换到向量的地方,但是我遇到了问题。我把它简化为:

#include <vector>

class A {
public:
A(void);
~A(void);

private:
std::vector< std::vector<int> > a;
};

A::A(void) : a() {}

A::~A(void) {}

这给出了来自g ++(flags:-O2 -Wunsafe-loop-optimizations,版本4.4.3(Ubuntu 4.4.3-4ubuntu5)在Ubuntu 10.04 x86_64上)的以下警告:

/usr/include/c++/4.4/bits/stl_construct.h:在析构函数'A :: ~A()'中: /usr/include/c++/4.4/bits/stl_construct.h:92:警告:无法优化循环,循环计数器可能溢出

那么,Leo给出了什么?矢量类不应该跟踪它有多少元素?那么,如果这是被引用的“计数器”,它怎么会溢出? (编辑:这个问题或多或少已在下面得到解答,但是,对我来说,只留下必然结果:为什么还要警告库中的循环无法优化?为什么我应该这样做?最终用户,关注那个?)

修改

这是相关的相关代码(但正如我在评论中所说,我不明白为什么这应该是我的考虑因为我不应该担心库的实现):

/**
 * Destroy the object pointed to by a pointer type.
 */
template<typename _Tp>
  inline void
  _Destroy(_Tp* __pointer)
  { __pointer->~_Tp(); }

template<bool>
  struct _Destroy_aux
  {
    template<typename _ForwardIterator>
      static void
      __destroy(_ForwardIterator __first, _ForwardIterator __last)
    {
      for (; __first != __last; ++__first) // <-- this is line 92
        std::_Destroy(&*__first);
    }
  };

更新

好的,我期待两个答案中的一个:我在我的代码中做了一些巧妙的异常(但不一定错误),或者该消息非常字面地告诉我循环指向库中无法优化。感激地保证它不是前者,是否会报告来自gcc的自己的库预期行为的警告,还是应该报告? (似乎不值得提出另一个问题来问这个新问题的借口。)

更新2

好的,感谢所有信息人员:这是编译器中的一个错误。仅供参考,我从GNU(版本4.5.2)编译了一个不受干扰的编译器和c ++库,它没有显示这种行为(库代码是相同的),所以我猜这样的警告不应该出现。再次感谢大家。

3 个答案:

答案 0 :(得分:6)

这只意味着编译器不能证明它不会溢出。由于你有-O2,你要求对循环进行某些优化。只有在可以证明这些条件的情况下,才能完成其中一些优化。您要求的编译器警告告诉您某些循环无法优化(我猜它可能是矢量底层数组的内联破坏循环。

我认为你可以忽视这个警告。但是我不得不想知道为什么你首先在编译选项中要求这个警告?

答案 1 :(得分:2)

我想我终于得到了编译器所暗示的内容。

循环不是在整数上执行的,所以我认为编译器试图告诉他的是它实际上无法计算循环将执行的运行次数。

您使用的警告实际上通常与来自gcc optimize options page-funsafe-loop-optimizations结合使用:

  

-funsafe-loop-optimizations       如果给定,循环优化器将假定循环索引不会溢出,并且具有非平凡退出条件的循环不是无限的。即使循环优化器本身无法证明这些假设是有效的,这也可以实现更广泛的循环优化。使用-Wunsafe-loop-optimizations`,编译器会在发现这种循环时发出警告。

我必须承认我认为编译器总是被允许假设一个循环最终会终止,因为推导它会需要解决在一般情况下无法解决的暂停问题。

但是在这里,如果你看一下这个函数,就不可能证明循环不会因为一个简单的原因而溢出:范围firstlast可能是格式错误。

  • 可能是first > last(注意:人们无法比较前向迭代器)
  • 可能是对齐*关闭,指针(T*++等同于+= sizeof(T),因为使用了!=而不是{{ 1}},如果是这种情况,<可以跳过first

当然,它在实践中永远不会发生,因为库编写者已经确保他们向该方法传递了正确的参数,但编译器无法推断它,因此警告。

我仍然感到惊讶,我原本以为编译器编写者会故意忽略他们自己的库所引发的警告,因为他们不得不使用那些轻微的手,以便我们不这样做。

(*)这里没有记忆对齐,我只是暗示一个错误校准的last实际上可以指向T*并且一次推进T的事实实际上允许它回到sizeof(T)边界......

答案 2 :(得分:1)

你的代码是无辜的。

警告必须是gcc编译器(至少是您当前拥有的版本)或它们使用的stl实现的错误。您应该完全没有关于正确代码的警告。