4

我一直在我clang 3.3的各种项目上运行静态分析器。除了一些是我自己的错的问题(这是意料之中的,否则我会非常伤心和自鸣得意),除了以下关于std::function'移动构造函数的问题是误报之外,一切都进行得很顺利。

在进一步讨论之前,这里有一个简单的测试用例:

int main() {
  std::function<void ()> f1;
  std::function<void ()> f2 = std::move(f1);
}

运行它clang++ -std=c++11 --analyze -Xanalyzer -analyzer-output=text foo.cpp(它使用 GCC 的libstdc++- 即 4.8.1 版本 - 而不是clang's libc++),你会得到以下跟踪:

In file included from foo.cpp:1:
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/iostream:39:
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/ostream:38:
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/ios:40:
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/char_traits.h:39:
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_algobase.h:64:
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_pair.h:59:
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/move.h:175:7: warning: Assigned value is garbage or undefined
      _Tp __tmp = _GLIBCXX_MOVE(__a);
      ^~~~~~~~~   ~~~~~~~~~~~~~~~~~~
foo.cpp:30:31: note: Calling move constructor for 'function'
  std::function<void ()> f2 = std::move(f1);
                              ^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:2232:2: note: Calling 'function::swap'
        __x.swap(*this);
        ^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:2359:2: note: Calling 'swap'
        std::swap(_M_invoker, __x._M_invoker);
        ^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/move.h:175:19: note: Calling 'move'
      _Tp __tmp = _GLIBCXX_MOVE(__a);
                  ^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/move.h:142:30: note: expanded from macro '_GLIBCXX_MOVE'
#define _GLIBCXX_MOVE(__val) std::move(__val)
                             ^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/move.h:175:19: note: Returning from 'move'
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/move.h:142:30: note: expanded from macro '_GLIBCXX_MOVE'
#define _GLIBCXX_MOVE(__val) std::move(__val)
                             ^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/move.h:175:7: note: Assigned value is garbage or undefined
      _Tp __tmp = _GLIBCXX_MOVE(__a);
      ^
1 warning generated.

如您所见,移动构造函数std::function(std::function&&)是根据swap. 整个操作的步骤是(如果我们相信的话clang):

  • f1构造正确
  • f2尚未构建,因此包含垃圾
  • f1搬进去了f2,但实际上它真的是swap
  • f2now 包含 old f1,但f1包含 old f2ie。垃圾
  • 在某些时候,f1包含垃圾被破坏......然后会发生什么?

理论说,这是非常糟糕的。在实践中,从实现来看,似乎std::function私有继承_Function_base会注意将所有重要的东西(即_M_manager)初始化为 null,因此clang关于 的警告_M_invoker是毫无意义的。

万一有人怀疑,移动一个对象应该让它处于不确定的状态,你只能分配给它或破坏它。GCC 的function实现正是这样做的:仅_M_manager在涉及资源管理方面很重要,其余的(包括_M_invoker)只是“方便”指针。

我在 GCC 的实施中挖得足够远,对' 诊断的误报function状态完全没有疑问。但是由于我的代码中有数百个地方发生了这种情况,因此至少可以说,通过静态分析器的结果非常痛苦。clang

我如何指示clang不要报告这个问题?

同样,如果您错过了它,我将clang 3.3与 GCC 一起使用libstdc++ 4.8.1


注意:如果您正在运行clang 3.4构建并且没有触发此误报,请告诉我。据我尝试,我还无法在我的系统上运行 3.4(Debian Jessie),但如果它解决了这个问题,我会更加努力。

4

1 回答 1

0

这可能对您来说很有趣: 分析仪的未来方向

基本上,您目前只能禁用每个 TU 的某些检查器或使用

#ifndef __clang_analyzer__
...
#endif

如果你真的需要。但当然应该报告实际的误报。

于 2014-02-21T21:30:17.693 回答