我能否确保 RVO 用于重新转换的值?

Can I ensure RVO for reintrepret-cast'ed values?

本文关键字:转换 新转换 确保 RVO 用于      更新时间:2023-10-16

假设我写了:

Foo get_a_foo() {
return reinterpret_cast<Foo>(get_a_bar());
}

并假设sizeof(Foo) == sizeof(Bar).

返回值优化是否必须在这里进行,或者当我使用 reinterpret_cast "违反规则"时,编译器是否可以做任何他们喜欢的事情?如果我没有得到 RVO,或者不能保证它 - 我可以更改此代码以确保它发生吗?

我的问题是关于 C++11 和单独的 C++17(如果我没记错的话,它在 r.r.t. RVO 中有一些变化)。

假设我写了:

Foo get_a_foo() {
return reinterpret_cast<Foo>(get_a_bar());
}

并假设sizeof(Foo) == sizeof(Bar).

reinterpret_cast对所有可能的FooBar类型都不合法。它仅适用于以下情况:

  1. Bar是一个指针,Foo是一个指针或一个足够大的整数/枚举,以容纳指针。
  2. Bar是一个足够大的整数/枚举,可以容纳指针,Foo是一个指针。
  3. Bar是对象类型,Foo是引用类型。

还有其他一些我没有介绍的情况,但它们要么无关紧要(nullptr_t铸造),要么属于#1或#2的类似问题。

看,在处理基本类型时,省略实际上并不重要。您无法区分省略基本类型的复制/移动和不省略它之间的区别。那么那里有转换吗?编译器是否仅使用返回值寄存器?这取决于编译器,通过"好像"规则。

并且 elision 在返回引用类型时不适用,因此 #3 已退出。

但是,如果FooBar是用户定义的对象类型(或指针、整数或成员指针以外的对象类型),则强制转换格式不正确reinterpret_cast不是某种微不足道的memcpy转换函数。

因此,让我们用一些可以实际工作的代码来替换它:

Foo get_a_foo()
{
return std::bit_cast<Foo>(get_a_bar());
}

其中C++20std::bit_cast有效地将一个平凡的可复制类型转换为另一个平凡的可复制类型。

这种转变仍然不会被取消。或者至少,不是通常使用"省略"的方式。

因为这两种类型是平凡可复制的,bit_cast只会调用平凡构造函数,所以编译器当然可以擦除构造函数,甚至使用get_a_foo的返回值对象作为get_a_bar的返回值对象。因此,它可以被认为是"省略"。

但是"elision"通常是指标准中允许实现忽略甚至不平凡的构造函数/析构函数的部分。编译器只能执行上述操作,因为所有构造函数和析构函数都是微不足道的。如果它们是非平凡的,它们就不能被忽视(再说一次,如果它们是非平凡的,std::bit_cast是行不通的)。

我的观点是,上述转换的优化不是由于"省略"或RVO规则;它完全是由于"好像"规则。即使在 C++17 中,bit_cast调用是否有效地进行 noop 也完全取决于编译器。是的,在创建Fooprvalue 后,C++17 需要将其复制到函数的返回值对象中的"省略"。

但皈依本身并不是一个消除的问题。