我一直在我的各个项目上运行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
的{{1}}),您将获得以下跟踪:
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
,但f1
包含旧的f1
ie。垃圾f2
被破坏......那么会发生什么?理论上说,这非常糟糕。在实践中,查看实现时,f1
私有地继承自std::function
,它会将所有重要事项(即_Function_base
)初始化为null,因此_M_manager
关于clang
的警告毫无意义。
万一有人怀疑,移动一个对象应该让它处于一个不确定的状态,你只能分配它或破坏它。 GCC的_M_invoker
实现正是如此:只有资源管理才涉及function
,其余的(包括_M_manager
)只是“便利”指针。
我在GCC的_M_invoker
实施中挖得足够远,对function
诊断的误报的状态完全毫无疑问。但是,由于我的代码中有数百个地方发生这种情况,这使得通过静态分析器的结果至少可以说是非常痛苦。
如何指示clang
不报告此问题?
同样,如果你错过了,我正在使用clang
和GCC的clang 3.3
。
注意:如果您正在运行libstdc++ 4.8.1
版本,如果则不会触发此误报,请告知我们。据我所知,我无法在我的系统上运行3.4(Debian Jessie),但如果它解决了这个问题,我会更加努力。
答案 0 :(得分:0)
这可能对您有意义: Future directions for the analyzer
基本上,您目前只能为每个TU禁用某些检查程序或使用
#ifndef __clang_analyzer__
...
#endif
如果你真的需要。但当然应该报告实际的误报。