运行此问题中提供的代码:
Why move return an rvalue reference parameter need to wrap it with std::move()?
#include <string>
#include <iostream>
#include <utility>
template<typename T>
class TD;
class Widget {
public:
explicit Widget(const std::string& name) : name(name) {
std::cout << "Widget created with name: " << name << ".\n";
}
Widget(const Widget& w) : name(w.name) {
std::cout << "Widget " << name << " just got copied.\n";
}
Widget(Widget&& w) : name(std::move(w.name)) {
std::cout << "Widget " << name << " just got moved.\n";
}
private:
std::string name;
};
Widget passThroughMove(Widget&& w) {
// TD<decltype(w)> wType;
// TD<decltype(std::move(w))> mwType;
return std::move(w);
}
Widget passThrough(Widget&& w) {
return w;
}
int main() {
Widget w1("w1");
Widget w2("w2");
Widget wt1 = passThroughMove(std::move(w1));
Widget wt2 = passThrough(std::move(w2));
return 0;
}
根据我使用的编译器,会产生不同的结果。使用最新的Visual Studio编译时(同时尝试了C ++ 14和C ++ 17),我得到以下结果:
Widget created with name: w1.
Widget created with name: w2.
Widget w1 just got moved.
Widget w2 just got moved. //<---
Ran在线编译该代码,结果有所不同。在提供的问题中,用户还收到相同的结果:
Widget created with name: w1.
Widget created with name: w2.
Widget w1 just got moved.
Widget w2 just got copied. //<---
为什么在使用Visual Studio时移动w2并在使用各种不同的编译器时复制w2?
在Visual Studio C ++文档的Microsoft C++ language conformance table部分中可以看到,这是Visual Studio实现的
[C0起,编译器MSVC版本19.24。
P1825R0的基本部分已添加到Visual Studio 2019 version 16.4 [重点我的]:
一个隐式可移动实体是自动存储的变量 非易失性对象或右值引用的持续时间 非易失性对象类型。在下面的复制初始化中 在上下文中,可以使用移动操作代替副本 操作:
- (3.1)如果[class.copy.elision]/3([stmt.return])或
return
([stmt.return.coroutine])中的expression语句是(可能 带括号的)id-expression表示一个隐式可移动的 在实体的主体或parameter-declaration-clause中声明的实体 最内层的封闭函数或lambda-expression,或- ((3.2)...
重载分辨率选择其构造函数副本或 [
co_return
重载以调用首先执行,就好像表达式或操作数是一个右值。 [...]。
使用return_value
和以下虚构示例
godbolt.ms
我们可以特别检查#include <memory>
struct Foo {
Foo(int num) : num(num) {}
Foo(const Foo& f) : num(f.num) {}
Foo(Foo&& f) : num(std::move(f.num)) {}
Foo& operator=(const Foo&) = delete;
Foo& operator=(Foo&&) = delete;
int num;
};
Foo maybeConsumeMyPreciousFoo(Foo&& foo, bool consume_foo) {
if (consume_foo) { return std::move(foo); }
else { return foo; } // Should not move.
}
函数中行else { return foo; }
的生成的装配;
对于MSVC v19.23:
maybeConsumeMyPreciousFoo(...)
并且对于MSVC v19.24:
mov rdx, QWORD PTR foo$[rsp]
mov rcx, QWORD PTR __$ReturnUdt$[rsp]
call Foo::Foo(Foo const &) ; Foo::Foo
mov rax, QWORD PTR __$ReturnUdt$[rsp]
分别显示,对于后者的版本,该分支实际上是从mov rdx, QWORD PTR foo$[rsp]
mov rcx, QWORD PTR __$ReturnUdt$[rsp]
call Foo::Foo(Foo &&) ; Foo::Foo
mov rax, QWORD PTR __$ReturnUdt$[rsp]
参数移出的,就好像它是一个右值。
GCC:
foo
:[...]
C ++ 2a语言功能
[...]
DR:更多隐式移动(合并P0527R1和P1155R3)
是否在GCC中可用?:否
Clang:
Bug 91427 - Implement P1825R0, Merged wording for P0527R1 and P1155R3
[...]
C ++ 20实施状态
P1825R0甚至没有列出。
最后,C++ Support in Clang还将P1825R0列为Clang和GCC的不支持。