bugprone-move-forwarding-reference

如果在转发引用上调用 std::move,则会发出警告,例如

template <typename T>
void foo(T&& t) {
  bar(std::move(t));
}

转发引用 通常应传递给 std::forward 而不是 std::move, 这是建议的修复方法。

(转发引用是类型为推导的函数模板参数的右值引用。)

在此示例中,建议的修复方法是

bar(std::forward<T>(t));

背景

上面示例中的代码有时会以 T&& 无论为 T 推导出什么类型,最终始终为右值引用,因此不可能将左值传递给 foo() 的预期编写。但是,这不是真的。考虑以下示例

std::string s = "Hello, world";
foo(s);

此代码编译,并且在调用 foo() 后, s 处于不确定的状态,因为它已被移动。这对于 foo() 的调用者来说可能是令人惊讶的,因为在调用 foo() 时没有使用 std::move

这种行为的原因在于函数模板(例如 foo())上的模板参数推导特殊规则,即在接受类型为推导的函数模板参数的右值引用参数的函数模板上。(请参阅 C++11 标准中的 [temp.deduct.call]/3 节。)

如果在左值上调用 foo()(如上面的示例),则 T 被推导出为左值引用。在示例中, T 被推导出为 std::string &。因此,参数 t 的类型变为 std::string& &&;根据引用折叠规则,这将折叠为 std::string&

这意味着 foo(s) 调用将 s 作为左值引用传递,并且 foo() 最终会移动 s,从而将其置于不确定的状态。