考虑下面的函数:Return语句异常
Widget f(Widget w) {
return w;
}
假设Widget
同时实现复制和移动的构造,根据C++标准,w
必须被视为在返回语句右值对象,如果编译器不考虑复制elision一个更好的选择。
在另一方面,考虑下面的版本:
Widget f(Widget&& w) {
return w;
}
至于对面的第一个版本,根据Effective Modern C++
Item 25
,笔者似乎暗示返回w
肯定会调用拷贝构造函数。换句话说,他建议返回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
'w'中的w都是左值(可能被复制为回报)。主要区别是函数参数,第一个是副本(!),第二个不是。将std :: move应用到第二个函数应该使它(几乎?)成为noop。 – 2015-02-17 22:45:50
@MattMcNabb它确实是对的。引用有一个例外,必须有。制作Widget f(Widget&w){return w; }'无声地移动将是危险的。现在,如果仅仅是左值引用,那么该异常*可能会好起来,但它仅仅适用于所有不知道从哪里移出的安全信息。 – hvd 2015-02-17 22:46:09
@hvd'return w;'总是将参数复制到返回值 - 无论它是Widget w',Widget&w'还是'Widget && w'。 (至少,这就是我的编译器告诉我的)。然后可以将返回值移到它分配的对象中;这一举动通常会被取消。 – 2015-02-17 22:53:09