不同的构造函数调用取决于编译器
Different constructor calls depending on compiler
运行 本题提供的代码:
#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. //<---
运行 和在线编译这段代码,结果不同。在提供的问题中,用户也收到相同的结果:
Widget created with name: w1.
Widget created with name: w2.
Widget w1 just got moved.
Widget w2 just got copied. //<---
为什么在使用 Visual Studio 时移动 w2 而在使用各种不同的编译器时复制 w2?
MSVC 已实现 P1825R0 - 更多隐式移动
如 Visual Studio C++ 文档的 Microsoft C++ language conformance table 部分所示,这是 Visual Studio 实现
截至 Visual Studio 2019 version 16.4,编译器 MSVC 版本 19.24。
P1825R0 的基本部分已添加到 [class.copy.elision]/3[强调 我的]:
An implicitly movable entity is a variable of automatic storage
duration that is either a non-volatile object or an rvalue reference
to a non-volatile object type. In the following copy-initialization
contexts, a move operation might be used instead of a copy
operation:
- (3.1) If the expression in a
return
([stmt.return]) or co_return
([stmt.return.coroutine]) statement is a (possibly
parenthesized) id-expression that names an implicitly movable
entity declared in the body or parameter-declaration-clause of the
innermost enclosing function or lambda-expression, or
- (3.2) ...
overload resolution to select the constructor for the copy or
the return_value
overload to call is first performed as if the expression or operand were an rvalue. [...].
使用 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.
}
我们可以特别检查 maybeConsumeMyPreciousFoo(...)
函数中行 else { return foo; }
生成的程序集;
对于 MSVC v19.23:
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]
对于 MSVC v19.24:
mov rdx, QWORD PTR foo$[rsp]
mov rcx, QWORD PTR __$ReturnUdt$[rsp]
call Foo::Foo(Foo &&) ; Foo::Foo
mov rax, QWORD PTR __$ReturnUdt$[rsp]
分别表明,对于后一个版本,分支实际上从 foo
参数移动,就好像它是右值一样。
GCC 和 Clang 尚未实现 P1825R0
海湾合作委员会:
C++ Standards Support in GCC:
[...]
C++2a Language Features
[...]
DR: More implicit moves (merge P0527R1 and P1155R3)
Available in GCC?: No
叮当声:
C++ Support in Clang
[...]
C++20 implementation status
P1825R0 not even listed.
最后,cppreference's C++ compiler support page 还将 P1825R0 列为不受 Clang 和 GCC 支持。
运行 本题提供的代码:
#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. //<---
运行 和在线编译这段代码,结果不同。在提供的问题中,用户也收到相同的结果:
Widget created with name: w1.
Widget created with name: w2.
Widget w1 just got moved.
Widget w2 just got copied. //<---
为什么在使用 Visual Studio 时移动 w2 而在使用各种不同的编译器时复制 w2?
MSVC 已实现 P1825R0 - 更多隐式移动
如 Visual Studio C++ 文档的 Microsoft C++ language conformance table 部分所示,这是 Visual Studio 实现
截至 Visual Studio 2019 version 16.4,编译器 MSVC 版本 19.24。
P1825R0 的基本部分已添加到 [class.copy.elision]/3[强调 我的]:
An implicitly movable entity is a variable of automatic storage duration that is either a non-volatile object or an rvalue reference to a non-volatile object type. In the following copy-initialization contexts, a move operation might be used instead of a copy operation:
- (3.1) If the expression in a
return
([stmt.return]) orco_return
([stmt.return.coroutine]) statement is a (possibly parenthesized) id-expression that names an implicitly movable entity declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, or- (3.2) ...
overload resolution to select the constructor for the copy or the
return_value
overload to call is first performed as if the expression or operand were an rvalue. [...].
使用 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.
}
我们可以特别检查 maybeConsumeMyPreciousFoo(...)
函数中行 else { return foo; }
生成的程序集;
对于 MSVC v19.23:
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]
对于 MSVC v19.24:
mov rdx, QWORD PTR foo$[rsp]
mov rcx, QWORD PTR __$ReturnUdt$[rsp]
call Foo::Foo(Foo &&) ; Foo::Foo
mov rax, QWORD PTR __$ReturnUdt$[rsp]
分别表明,对于后一个版本,分支实际上从 foo
参数移动,就好像它是右值一样。
GCC 和 Clang 尚未实现 P1825R0
海湾合作委员会:
C++ Standards Support in GCC:
[...]
C++2a Language Features
[...]
DR: More implicit moves (merge P0527R1 and P1155R3)
Available in GCC?: No
叮当声:
C++ Support in Clang
[...]
C++20 implementation status
P1825R0 not even listed.
最后,cppreference's C++ compiler support page 还将 P1825R0 列为不受 Clang 和 GCC 支持。