2015-02-17 104 views
9

考虑下面的函数: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 
+0

'w'中的w都是左值(可能被复制为回报)。主要区别是函数参数,第一个是副本(!),第二个不是。将std :: move应用到第二个函数应该使它(几乎?)成为noop。 – 2015-02-17 22:45:50

+3

@MattMcNabb它确实是对的。引用有一个例外,必须有。制作Widget f(Widget&w){return w; }'无声地移动将是危险的。现在,如果仅仅是左值引用,那么该异常*可能会好起来,但它仅仅适用于所有不知道从哪里移出的安全信息。 – hvd 2015-02-17 22:46:09

+0

@hvd'return w;'总是将参数复制到返回值 - 无论它是Widget w',Widget&w'还是'Widget && w'。 (至少,这就是我的编译器告诉我的)。然后可以将返回值移到它分配的对象中;这一举动通常会被取消。 – 2015-02-17 22:53:09

回答

5

用于基于当复制省略允许对隐含移动标准,但也有一些例外。复制elision被允许用于不是函数参数或捕获参数的本地对象。隐式移动添加了作为函数参数的本地对象。所以它允许它在Widget f(Widget w) { return w; },但不在Widget f(Widget&& w) { return w; },其中w是一个局部变量,但不是本地对象。

在当前不允许的情况下隐式移动是安全的,包括在您的示例中。但是,隐性移动是根据具体情况仔细考虑的,您的示例要么尚未考虑,要么尚未被视为安全。

非常类似的例子Widget f(Widget& w) { return w; }将是不安全:呼叫者做Widget w; f(w);不能指望w默默由编译器清除出去。

这个非常相似的例子显示了启用隐式移动是多么的冒险,以及为什么你必须拼出来,即使它看起来很明显。涉及“局部变量”的措辞涵盖Widget&& w也将覆盖Widget& w。在不安全的情况下进行隐式移动要比隐式移动安全的情况要糟得多,必须绝对确定文字只有覆盖安全的情况。

+0

我不认为复制elision在返回函数参数时被允许。 – juanchopanza 2015-02-17 22:58:07

+0

@ hvd:这就是我想了解的:如果它是“不考虑”或“不安全”。在后一种情况下,我想明白为什么。 “非常相似”的例子显然是不安全的。 – Martin 2015-02-17 23:01:24

+1

@juanchopanza糟糕...你说的很对,我的回答是错误的。这种例外情况不在复制方面,这是标准需要隐式转移的另一种情况。将编辑。 – hvd 2015-02-17 23:02:41