编译器是否可以自动生成std::move用于最后一次使用左值

Can compiler generate std::move for a last use of lvalue automatically?

本文关键字:最后一次 用于 move 自动生成 std 编译器 是否      更新时间:2023-10-16

这样的代码经常出现在r值参考文章中:

Dave Abrams: Move It With右值引用

void g(X);
void f()
{
    X b;
    g(b);              // still need the value of b
    …
    g( std::move(b) ); // all done with b now; grant permission to move
}

编译器是否可以自动生成这种优化,即检测l值无论如何都将被销毁并且可以从其中移动,或者这是否违反标准,假设编译器不知道如何为X类实现移动,复制或销毁?

如果这种优化是允许的,它是由一些编译器在实践中执行的吗?

No。考虑:

using X = std::shared_ptr<int>;
void g(X);
void f() {
    X b = std::make_shared<int>();
    int &i = *b;
    g(b);              // last use of 'b'
    i = 5;
}

一般来说,编译器不能假设改变X的拷贝、移动和析构函数的语义是合法的,而不对使用b的所有代码(即整个fg和其中使用的所有类型)进行分析。

事实上,在某些情况下,整个程序的分析可能是必要的:

using X = std::shared_ptr<std::lock_guard<std::mutex>>;
std::mutex i_mutex;
int i;
void g(X);
void f() {
    X b = std::make_shared<std::lock_guard<std::mutex>>(i_mutex);
    g(b);              // last use of 'b'
    i = 5;
}
如果移动了b,这将引入与其他使用i_mutex同步访问i的线程之间的数据竞争。

编译器可以做到吗?只能作为显式语言扩展,因为标准不允许他们在没有显式语言扩展的情况下进行这种优化。

他们应该这样做吗?不。g(b)的含义应以gb的定义为基础。g应该是某种可调用类型,它具有重载,该重载接受b可以隐式转换为的内容。给定对所有可用g s的定义和b的定义的访问权,您应该能够准确地确定将调用什么函数。

允许这种"优化"现在意味着这是不可能的。g(b) 可能执行移动,也可能不执行移动,这取决于g(b)恰好在函数中的位置。这可不是件好事。

return被允许使用它,但只是因为它仍然具有相同的含义。

如果b是一个生命周期限制在函数范围内的值类型,那么return b将总是尝试从b移出。

(…)假设一个通用的情况下,编译器不知道任何关于如何移动,复制或销毁实现的X类?

不,编译器不允许基于信仰进行优化。

为清楚起见,这个问题与副本省略无关:编译器可以允许省略一个副本,但它们不能随意地将副本更改为移动。