Я бегал 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
теперь содержит старый f1
, но f1
содержит старый f2
то есть. мусорf1
содержащий мусор уничтожается … что происходит потом?Теория говорит, что это очень плохо. На практике, глядя на реализацию, кажется, что std::function
в частном порядке наследует от _Function_base
который заботится о инициализации всего, что имеет значение (т.е. _M_manager
) к нулю, так clang
предупреждение о _M_invoker
бессмысленно.
На всякий случай, если кто-то сомневается, перемещение объекта должно оставить его в неопределенном состоянии, где вы можете либо назначить его, либо уничтожить. ССЗ function
реализация делает именно это: только _M_manager
важно, насколько управление ресурсами вовлечено, остальное (включая _M_invoker
) — это просто «удобные» указатели.
Я вырыл достаточно далеко в GCC function
реализация не имеет абсолютно никаких сомнений в отношении статуса ложно положительный из clang
Диагностика. Но так как у меня есть буквально сотни мест, где это происходит в моем коде, это делает просмотр результатов статического анализатора довольно болезненным, если не сказать больше.
Как я могу наставлять clang
не сообщать об этой самой проблеме?
Опять же, если вы пропустили это, я использую clang 3.3
наряду с GCC libstdc++ 4.8.1
,
Замечания: Если вы работаете clang 3.4
построить и если это не вызывает этот ложный позитив, пожалуйста, дайте мне знать. Насколько я пытался, я не смог запустить 3.4 на своей системе (Debian Jessie), но если это решит эту проблему, я просто попробую больше.
Это может быть интересно для вас:
Будущие направления для анализатора
По сути, в настоящее время вы можете отключить только определенные контролеры для TU или использовать
#ifndef __clang_analyzer__
...
#endif
если вам действительно нужно. Но, конечно, фактические ложные срабатывания должны сообщаться.
Других решений пока нет …