我一直在我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
f2
now 包含 oldf1
,但f1
包含 oldf2
ie。垃圾- 在某些时候,
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),但如果它解决了这个问题,我会更加努力。