0

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?

francesco
  • 7,189
  • 7
  • 22
  • 49

2 Answers2

1

When you do

f(std::forward<ArgsType>(args)...);

You are passing all of args... to f using perfect forwarding. This means that if any of args... is an rvalue, and if f moves from that rvalue into either a function parameter or a variable inside the function, then that object in args... will now be in a moved from state, meaning its value is now gone. When you then reuse args...

g<1>(std::forward<F>(f), std::forward<ArgsType>(args)...);

You could now be using an object that has already had it's contents moved, and is no longer usable for reading. This is why you get the warning. If you want to call both functions, you can't use perfect forwarding.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
1

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.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458