【发布时间】:2017-08-24 14:03:23
【问题描述】:
std::forward 是一个条件转换,为什么当编译器看到用户试图传递给其他函数的参数是通用引用时,它不能自动完成工作。
意味着为什么编译器会将做正确事情的责任放在用户身上,写成std::forward。
以有效的现代 C++ 为例。
void process(const Widget& lvalArg); // process lvalues
void process(Widget&& rvalArg); // process rvalues
template<typename T> // template that passes
void logAndProcess(T&& param) // param to process
{
auto now = // get current time
std::chrono::system_clock::now();
makeLogEntry("Calling 'process'", now);
process(std::forward<T>(param));
}
在上面的代码示例中,我知道删除 std::forward 将为 process 选择不正确的重载,但要正确选择正确的重载,为什么用户需要编写 std::forward,我的意思是编译器不能为对于不想做正确事情的用户,我们可以使用std::dont_forward。
我可能会遗漏一些用例,其中编译器可能会对正确的内容感到困惑,但在上述情况下,param 是通用引用并且给编译器的两个进程重载我看不到任何混淆。
只是为了解释这个问题的不同之处,它不是关于为什么我们在当前编译器行为中需要“std::forward”,而是为什么编译器默认情况下不能做明显的事情,即在传递转发引用时调用正确的函数重载around ,包括检测多次使用和在最后一次使用时转换为右值。
【问题讨论】:
-
默认行为更安全。假设我想在
logAndProcess中呼叫process两次。如果您遗漏了std::forward,param将被复制。相反,如果行为是您想要的,那么对process的第二次调用将有一个param,它可能已从第一次调用process移出。此外,通用引用现在称为转发引用。 -
@Praetorian ,所以如果你使用 std::forward 你确定第二次使用是否安全?我的意思是即使使用 std::forward 你也不知道参数发生了什么,无论它是被移动还是复制,因为你不知道函数是如何被左值或右值调用的。
-
@user8063157 - 不。但是如果默认情况下总是移动右值,你只能使用它们一次,然后它们就消失了。现在你必须用
std::move或std::forward明确标记你的最后一次使用,让它们“继续”。 -
@Bopersson ,最后与 std::forward 一起使用不会对 In between calls 造成不公正,哪个会复制?话虽如此,编译器还可以检测最后一次使用转发引用的时间并做正确的事情!
-
正确的是什么?也许在我展示的示例中,我希望对
process的第一次调用有条件地从param移动,并且我有一些方法可以在第二次调用中检测到它并采取相应的行为。所以在第一种情况下复制并在第二种情况下转发不是我想要的。编译器无法读懂程序员的想法,因此正常的默认设置是在所有情况下都以安全的方式行事,除非另有说明。