为什么编译器不会自动推断出变量即将超出范围,因此将其视为右值引用?
以这段代码为例:
#include <string>
int foo(std::string && bob);
int foo(const std::string & bob);
int main()
{
std::string bob(" ");
return foo(bob);
}
检查汇编代码清楚地表明,const & 版本的“foo”在函数末尾被调用。
此处的编译器资源管理器链接:https://godbolt.org/g/mVi9y6
编辑:澄清一下,我不是在寻找有关移动变量的替代方法的建议。我也不想理解为什么编译器选择 foo 的 const& 版本。这些是我理解得很好的事情。
我有兴趣了解一个反例,其中编译器在变量超出范围之前将其最后一次使用转换为右值引用会在生成的代码中引入严重的错误。如果编译器实现这种“优化”,我想不出代码会中断。
如果当编译器自动将最后一次使用即将超出范围的变量作为右值引用时没有中断代码,那么为什么编译器不将其实现为优化?
我的假设是一些代码会破坏编译器实现“优化”的地方,我想知道这些代码是什么样的。
我上面详述的代码是一个代码示例,我相信它会从这样的优化中受益。
函数参数的计算顺序,例如 operator+(foo(bob), foo(bob)) 是实现定义的。因此,代码如
return foo(bob) + foo(std::move(bob));
是危险的,因为您使用的编译器可能会首先评估 + 运算符的右侧。这将导致字符串 bob 可能被移动,并使其处于有效但不确定的状态。随后,将使用生成的修改后的字符串调用 foo(bob)。
在另一个实现中,可能首先评估非移动版本,并且代码将按照非专家所期望的方式运行。
如果我们假设某些 future 版本的 c++ 标准实现了允许编译器将变量的最后一次使用视为右值引用的优化,那么
return foo(bob) + foo(bob);
毫无意外地工作(假设 foo 的适当实现,无论如何)。
这样的编译器,无论它对函数参数使用什么求值顺序,总是会在这种情况下将 bob 的第二次(也是最后一次)用法作为右值引用来求值,无论它是左侧,还是运算符 + 的右侧。
最佳答案
这是一段完全有效的现有代码,但您的更改会破坏该代码:
// launch a thread that does the calculation, moving v to the thread, and
// returns a future for the result
std::future<Foo> run_some_async_calculation_on_vector(std::pmr::vector<int> v);
std::future<Foo> run_some_async_calculation() {
char buffer[2000];
std::pmr::monotonic_buffer_resource rsrc(buffer, 2000);
std::pmr::vector<int> vec(&rsrc);
// fill vec
return run_some_async_calculation_on_vector(vec);
}
构造容器的移动总是传播其分配器,但复制构造容器则不必,polymorphic_allocator
是一个分配器,不会在容器复制构造上传播.相反,它总是恢复到默认内存资源。
这段代码在复制时是安全的,因为 run_some_async_calculation_on_vector
接收到从默认内存资源分配的拷贝(希望它在整个线程的生命周期中持续存在),但是被一个移动完全破坏了,因为那样的话它会保留 rsrc
作为内存资源,一旦 run_some_async_calculation
返回,它将消失。
关于c++ - 编译器推导超出范围的变量的右值引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45763943/