16

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

Dave Abrams:使用右值引用移动它

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
}

编译器是否可以自动生成这种优化,即检测一个左值无论如何都会被破坏并且可以被移除,或者这是否违反标准,假设编译器不知道任何关于如何是的为 X 类实现的移动、复制或破坏?

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

4

3 回答 3

12

不。考虑:

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(即整个f,g以及其中使用的所有类型)进行分析。

实际上,在某些情况下,可能需要进行全程序分析:

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被移动,这将引入与同步访问iusing的其他线程的数据竞争i_mutex

于 2013-03-13T14:07:20.110 回答
4

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

他们应该这样做吗?不, 的含义g(b)应该基于 和 的g定义bg应该是一些可调用的类型,它有一个b可以隐式转换成的重载。如果可以访问所有可用gs 的定义和 s 的定义b,您应该能够准确地确定将调用哪个函数。

现在允许这种“优化”意味着这是不可能的。g(b) 可能会执行移动,也可能不会移动,具体取决于g(b)函数中的确切位置。这不是一件好事。

return被允许逃脱它,但只是因为它仍然具有相同的含义。return b将始终尝试从bifb是一个生命周期受限于函数范围的值类型中移动。

于 2013-03-13T14:05:27.497 回答
4

(...) 假设一个通用情况,编译器不知道如何为 X 类实现移动、复制或破坏?

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

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

于 2013-03-13T14:00:22.830 回答