On the following code
#include <utility>
template <int i = 0, class F, typename... ArgsType>
void g(F&& f, ArgsType&&... args)
{
if constexpr (i < 1) {
f(std::forward<ArgsType>(args)...);
g<1>(std::forward<F>(f), std::forward<ArgsType>(args)...);
}
}
a run of cppcheck --enable=all
gives the following warning:
Checking test.hpp ...
test.hpp:8:53: warning: Access of forwarded variable 'args'. [accessForwarded]
g<1>(std::forward<F>(f), std::forward<ArgsType>(args)...);
^
test.hpp:7:7: note: Calling std::forward(args)
f(std::forward<ArgsType>(args)...);
^
test.hpp:8:53: note: Access of forwarded variable 'args'.
g<1>(std::forward<F>(f), std::forward<ArgsType>(args)...);
^
What does this warning mean and why is it triggered?
It's warning you that your code uses a variable after a potential move.
std::vector<int> v{1, 2, 3};
g([](auto v_) { }, std::move(v));
We pass v
into the function by moving (so we pass an rvalue reference to it). When it will be forwarded in the invocation f(std::forward<ArgsType>(args)...)
, the argument v_
will be initialized by stealing the internal of v
. Using it (via reference) again after that is ill-advised, since it's defined to be in a "valid, but unspecified state". Using it in such a state can potentially cause subtle and hard to detect problems.
Unless a type documents its okay to use its objects after they have been moved from, the only truly valid operation we can do is assign it a new value. Your code doesn't do that.