[英]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
来省略副本:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.