tidy - bugprone-move-forwarding-reference — Extra Clang Tools 22.0.0git documentation (original) (raw)
Warns if std::move is called on a forwarding reference, for example:
template void foo(T&& t) { bar(std::move(t)); }
Forwarding references should typically be passed to std::forward instead of std::move, and this is the fix that will be suggested.
(A forwarding reference is an rvalue reference of a type that is a deduced function template argument.)
In this example, the suggested fix would be
Background¶
Code like the example above is sometimes written with the expectation thatT&& will always end up being an rvalue reference, no matter what type is deduced for T, and that it is therefore not possible to pass an lvalue tofoo(). However, this is not true. Consider this example:
std::string s = "Hello, world"; foo(s);
This code compiles and, after the call to foo(), s is left in an indeterminate state because it has been moved from. This may be surprising to the caller of foo() because no std::move was used when callingfoo().
The reason for this behavior lies in the special rule for template argument deduction on function templates like foo() – i.e. on function templates that take an rvalue reference argument of a type that is a deduced function template argument. (See section [temp.deduct.call]/3 in the C++11 standard.)
If foo() is called on an lvalue (as in the example above), then T is deduced to be an lvalue reference. In the example, T is deduced to bestd::string &. The type of the argument t therefore becomesstd::string& &&; by the reference collapsing rules, this collapses tostd::string&.
This means that the foo(s) call passes s as an lvalue reference, andfoo() ends up moving s and thereby placing it into an indeterminate state.