繁体   English   中英

为什么 C++ 编译器在使用 RVO 将 POD 移动到可选时不能省略移动?

[英]Why can't the C++ compiler elide the move when moving a POD into an optional with RVO?

考虑以下代码 ( godbolt ):

#include <optional>
#include <array>

struct LargeType {
    std::array<int, 256> largeContents;
};

LargeType doSomething();

std::optional<LargeType> wrapIntoOptional(){
    return std::optional<LargeType> {doSomething()};
}

如您所见,有一个 function 返回一个大的 POD,然后一个 function 将它包装到一个std::optional中。 正如在 godbolt 中可见,编译器在这里创建了一个memcpy ,因此它不能完全忽略移动 object。这是为什么?

如果我理解正确,C++ 语言将允许由于 as-if 规则而忽略移动,因为它没有明显的副作用。 所以看来编译器真的避不开。 但为什么?

我(可能不正确)对编译器如何优化memcpy的理解是将对可选内部存储的引用传递给doSomething() (因为我猜这么大的对象无论如何都会通过隐藏引用传递)。 由于 RVO,可选本身已经位于wrapIntoOptional调用者的堆栈中。 由于std::optional的构造函数的定义在 header 中,它可供编译器使用,因此它应该能够内联它,因此它可以首先将存储位置交给doSomething 那么我的直觉有什么问题呢?

澄清一下:我不认为 C++ 语言需要编译器内联它。 我只是认为这将是一个合理的优化,并且考虑到将东西包装到可选项中是一个常见的操作,这将是一个在现代编译器中实现的优化。

不可能通过构造函数调用将任何复制/移动到由构造的 object 管理的存储中。

构造函数以 object 作为参考。 为了将引用绑定到某个东西,必须有一个 object,因此doSomething()的纯右值必须具体化为临时 object 以绑定到引用,然后构造函数必须从该临时复制/移动到它自己的存储中。

通过function参数是不可能省略的。 这将需要知道 function 的实现和 C++ 的指定方式,可以编译每个 function 只知道其他函数的声明(除了常量表达式评估)。 这会破坏它或在声明中需要一种新类型的注释。

这些都不能阻止编译器以不影响可观察行为的方式进行优化。 如果您的编译器没有弄清楚可以避免额外的副本,并且在查看所有相关函数/构造函数定义时没有明显的副作用,那么您可以向您的编译器供应商投诉。 复制省略的概念是允许编译器优化复制/移动,即使它会产生明显的副作用

您可以添加noexcept来省略副本:

https://godbolt.org/z/rrGEfrdzc

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM