Return 语句异常
Return statement anomaly
考虑以下函数:
Widget f(Widget w) {
return w;
}
假设 Widget
同时实现了复制和移动构造函数,根据 C++ 标准,w
必须被视为 rvalue 对象return 语句,以防编译器不认为复制省略是更好的选择。
另一方面,考虑以下版本:
Widget f(Widget&& w) {
return w;
}
与第一个版本相反,根据 Effective Modern C++
的 Item 25
,作者似乎在暗示 returning w
肯定会调用复制构造函数.换句话说,他建议改为 return std::move(w)
,以使编译器使用(可能更快的)移动构造函数。
你能解释一下为什么第二个版本的 f()
以 Widget&&
作为参数不等同于第一个版本以 Widget
作为被调用构造函数的值吗在 return 语句中,还考虑到在两个函数的主体中,表达式 w
指的是 lvalue
?
演示行为的完整示例:
#include <iostream>
struct Widget
{
Widget() { std::cout << "constructed" << std::endl; }
~Widget() { std::cout << "destructed" << std::endl; }
Widget(const Widget&) { std::cout << "copy-constructed" << std::endl; }
Widget(Widget&&) { std::cout << "move-constructed" << std::endl; }
};
Widget
f1(Widget w)
{
return w;
}
Widget
f2(Widget&& w)
{
return w;
}
int
main()
{
f1(Widget {});
std::cout << std::endl;
f2(Widget {});
}
输出:
constructed
move-constructed
destructed
destructed
constructed
copy-constructed
destructed
destructed
隐式移动的标准基于何时允许复制省略,但也有一些例外。对于不是函数参数或 catch 参数的局部对象,允许复制省略。隐式移动将作为函数参数的局部对象添加到其中。所以它允许它在 Widget f(Widget w) { return w; }
中,但在 Widget f(Widget&& w) { return w; }
中不允许,其中 w
是局部变量,但不是局部对象。
在当前不允许的几种情况下,隐式移动是安全的,包括在您的示例中。但是,隐式移动是根据具体情况仔细考虑的,您的示例尚未被考虑,或者尚未被认为是安全的。
非常类似的例子Widget f(Widget& w) { return w; }
是不安全的:调用者做Widget w; f(w);
不能指望w
被编译器。
这个非常相似的例子显示了启用隐式移动的风险有多大,以及为什么你必须把它拼出来,即使它对你来说似乎很明显。提到 "local variable" 的措辞将涵盖 Widget&& w
, 也 涵盖 Widget& w
。在不安全的地方进行隐式移动比在安全的地方错过隐式移动要糟糕得多,必须绝对确定措辞 only 涵盖安全情况。
考虑以下函数:
Widget f(Widget w) {
return w;
}
假设 Widget
同时实现了复制和移动构造函数,根据 C++ 标准,w
必须被视为 rvalue 对象return 语句,以防编译器不认为复制省略是更好的选择。
另一方面,考虑以下版本:
Widget f(Widget&& w) {
return w;
}
与第一个版本相反,根据 Effective Modern C++
的 Item 25
,作者似乎在暗示 returning w
肯定会调用复制构造函数.换句话说,他建议改为 return std::move(w)
,以使编译器使用(可能更快的)移动构造函数。
你能解释一下为什么第二个版本的 f()
以 Widget&&
作为参数不等同于第一个版本以 Widget
作为被调用构造函数的值吗在 return 语句中,还考虑到在两个函数的主体中,表达式 w
指的是 lvalue
?
演示行为的完整示例:
#include <iostream>
struct Widget
{
Widget() { std::cout << "constructed" << std::endl; }
~Widget() { std::cout << "destructed" << std::endl; }
Widget(const Widget&) { std::cout << "copy-constructed" << std::endl; }
Widget(Widget&&) { std::cout << "move-constructed" << std::endl; }
};
Widget
f1(Widget w)
{
return w;
}
Widget
f2(Widget&& w)
{
return w;
}
int
main()
{
f1(Widget {});
std::cout << std::endl;
f2(Widget {});
}
输出:
constructed
move-constructed
destructed
destructed
constructed
copy-constructed
destructed
destructed
隐式移动的标准基于何时允许复制省略,但也有一些例外。对于不是函数参数或 catch 参数的局部对象,允许复制省略。隐式移动将作为函数参数的局部对象添加到其中。所以它允许它在 Widget f(Widget w) { return w; }
中,但在 Widget f(Widget&& w) { return w; }
中不允许,其中 w
是局部变量,但不是局部对象。
在当前不允许的几种情况下,隐式移动是安全的,包括在您的示例中。但是,隐式移动是根据具体情况仔细考虑的,您的示例尚未被考虑,或者尚未被认为是安全的。
非常类似的例子Widget f(Widget& w) { return w; }
是不安全的:调用者做Widget w; f(w);
不能指望w
被编译器。
这个非常相似的例子显示了启用隐式移动的风险有多大,以及为什么你必须把它拼出来,即使它对你来说似乎很明显。提到 "local variable" 的措辞将涵盖 Widget&& w
, 也 涵盖 Widget& w
。在不安全的地方进行隐式移动比在安全的地方错过隐式移动要糟糕得多,必须绝对确定措辞 only 涵盖安全情况。