取决于编译器的不同构造函数调用

问题描述 投票:3回答:1

运行此问题中提供的代码:

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?

c++ visual-c++ rvalue-reference
1个回答
2
投票

MSVC已实现P1825R0-更多隐式移动

在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和Clang尚未实现P1825R0

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的不支持。

© www.soinside.com 2019 - 2024. All rights reserved.